Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ClosureExpressionVisitor::sortByField() fails when comparing Objects/Dates #361

Open
t11n opened this issue Sep 20, 2017 · 4 comments
Open

Comments

@t11n
Copy link

t11n commented Sep 20, 2017

In method ClosureExpressionVisitor::sortByField() we have the following sort function...

    return function ($a, $b) use ($name, $next, $orientation) {
        $aValue = ClosureExpressionVisitor::getObjectFieldValue($a, $name);
        $bValue = ClosureExpressionVisitor::getObjectFieldValue($b, $name);

        if ($aValue === $bValue) {
            return $next($a, $b);
        }

        return (($aValue > $bValue) ? 1 : -1) * $orientation;
    };

... which does not work with Objects, especially DateTime objects. I do not know if it makes sense to compare objects in general if they are greater or lesser, but it definitely makes sense for DateTime objects.

The problem is, that equal DateTime objects will not be recognised by $aValue === $bValue. Instead $aValue > $bValue will evaluate to false in these cases. Not a big deal you may think, as the a element is put below the b element, which is fine, as they are equal. That's right, but $next will neither be executed.

In my use case, sorting by date is the first order criteria, sorting by ID is the second. That code will never evaluate the second criteria, though.

Possible fix:

        if ($aValue instanceof \DateTime && $aValue == $bValue || $aValue === $bValue) {
            return $next($a, $b);
        }

You have to judge whether it makes sense to compare other object types. For DateTimes however, this is necessary, I suppose. Sorting by date with a second order by criteria is not possible otherwise.

Please have a look into the matter.

@lcobucci
Copy link
Member

@t11n comparing DateTime objects is actually fine, but maybe we should be using the space ship operator instead of ($aValue > $bValue) ? 1 : -1. But regardless of that, could you send a PR with a failing functional test? That would be really helpful!

You can find examples on https://github.com/doctrine/doctrine2/tree/388afb46d0cb3ed0c51332e8df0de9e942c2690b/tests/Doctrine/Tests/ORM/Functional/Ticket

@t11n
Copy link
Author

t11n commented Nov 27, 2017

@lcobucci Thanks for looking into the issue.

I prepared three tests, that demonstrate the problem: doctrine/orm#6851
(branched from v2.5, since I do not have a php7.1 setup running, yet)

First test shows sorting by scalar values, that works as expected. The other two tests sort by fields, where first ordered by field is a DateTime object and a general stdClass.

It is debatable whether sorting by any other objects, than DateTime makes sense. But since php does have a logic for that to evaluate, it is probably good to adopt it.

Something like that in ClosureExpressionVisitor::sortByField() makes the tests green:

if (is_object($aValue) && $aValue == $bValue || $aValue === $bValue) {
    return $next($a, $b);
}

It is the identical comparison, that prevents $next from being evaluated, whenever two objects are not identical.

@beberlei
Copy link
Member

beberlei commented Dec 8, 2020

The ExpressionBuilder has this class level comment about only "scalar" values being supported for interoperability. Obviously a comment is well hidden and hard to spot. We could throw an exception if values are not a scalar value to enforce this, for one big whopping BC break. But given the amount of issues about this it might be worthwhile.

@mpdude
Copy link

mpdude commented Jan 31, 2023

I may be wrong, but I believe this issue belongs to the doctrine/collections repo?

/cc @greg0ire

@greg0ire greg0ire transferred this issue from doctrine/orm Feb 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants