Skip to content

Conversation

ipundit
Copy link
Contributor

@ipundit ipundit commented Sep 14, 2023

Fixed bugs in select() related to repeated column names. See RelationTest::testSelectChainedSelects() for more details.

Implemented magic getters for Relation->last, first, and take so that you don't have to use parentheses like $relation->last(). Ruby on Rails supports this syntax, and now so do we.

Unfortunately, Model::last cannot be implemented because PHP has not implemented a __getStatic($name) magic function yet, only __get($name), so this is one example where we cannot by fully compliant to the RoR spec because of differences between PHP and Ruby.

ipundit added 30 commits August 31, 2023 18:10
…at capitalization for attribute names are retained. So the short form for New York is now NY instead of ny. test/model/Author, Book, and Venue now have those strtoupper() workarounds in getter/setter methods removed as it's done in Model.php
…rcased in Model.php in order to be case insensitive.
…at capitalization for attribute names are retained. So the short form for New York is now NY instead of ny. test/model/Author, Book, and Venue now have those strtoupper() workarounds in getter/setter methods removed as it's done in Model.php
…rcased in Model.php in order to be case insensitive.
…cord

# Conflicts:
#	test/ActiveRecordTest.php
#	test/models/Book.php
…Disable it so that regression does not timeout.
…d#35:

Authors table now has a column that is camel cased: firstName
Added tests that demonstrate case insensitivity when accessing this firstName column
- Correct link to contributing.md

public function testSelectChainedSelects()
{
$relation = Author::select('name,name')->select('author_id');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at this test, it's not clear to me what the bug is you're fixing. I'm not saying you're wrong, just that it's not clear. For starters, using reflection to dip into internals as proof that there is a problem is sort of breaking the rules. If you want to show a problem, do it with publicly available means. For instance:

$author = Author::select('name,name')->take();
$this->assertEquals(['name' => 'Tito'], $author->attributes() );

This is why I suggested starting with a failing test case for us to look at before diving in. Recommend starting a new branch that has a single failing test case, preferably performed without the kind of introspection you're doing here. Once we understand the situation better, we can talk about what the fix is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hopefully the new commit explains things better. It prevents:

SELECT name, name
FROM Author

and

SELECT name, *
FROM Author

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okie-doke, then 👍

@codecov
Copy link

codecov bot commented Sep 16, 2023

Codecov Report

Merging #69 (71e4187) into master (99c5f67) will increase coverage by 0.09%.
The diff coverage is 100.00%.

@@             Coverage Diff              @@
##             master      #69      +/-   ##
============================================
+ Coverage     96.17%   96.26%   +0.09%     
- Complexity      936      962      +26     
============================================
  Files            33       33              
  Lines          2274     2330      +56     
============================================
+ Hits           2187     2243      +56     
  Misses           87       87              
Files Changed Coverage Δ
lib/Model.php 98.03% <100.00%> (+0.03%) ⬆️
lib/Relation.php 100.00% <100.00%> (ø)

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@ipundit ipundit changed the title Relation magic functions and select fix Fixed select(), implemented pluck(), ids(), reselect(), none() Sep 16, 2023
Copy link
Contributor

@shmax shmax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking pretty good. 👍

lib/Model.php Outdated
@@ -1751,7 +1774,14 @@ protected static function Relation(array $options = []): Relation
*/
public static function count(/* ... */): int
{
return static::Relation()->count(...func_get_args());
$relation = static::Relation();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're going to do any work, please delegate to Relation, like we do everywhere else.

And didn't we remove support for passing conditions to count? I don't see any mention of that in the modern rails documentation:
https://apidock.com/rails/v5.2.3/ActiveRecord/Calculations/count

What prompted this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backwards compatibility for version 1. What I had in my original PR was Model::find() would have the complex support for version 1 parameters, and Relation::find() would only support what's in modern Ruby. If you want to break every single user of version 1 (ie: all of us) in version 2, then this is no longer necessary. The users of version 2 would do Model::where(complex input stuff)->count()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if someone wants to start a 1.x legacy branch and start back-porting fixes then that's up to them, but otherwise I'm not interested in bc; this API is confusing enough without muddying things further. Once we have our shiny new 2.x line, we will be more careful going forward.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so that's why you removed all those version 1 tests and threw away the code I wrote in my original PR to be version 1 compatible. If that's not a design goal, then that simplifies implementation, but will probably upset the entire version 1 user base as everybody will need to change their code. But that's the meaning of going from version 1 to 2. It was possible to keep backwards compatibility like I showed for count(). Anyway, removed backwards compatibility.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, sorry about that, but even if we did keep passing where clauses to find and count it still wouldn't make v2.0 compatible with 1.x (because, for example, I moved us away from mixed-array association configurations in the interest of static checking). I guess what we could do is find the earliest point in the commit history where all the tests pass and tag it with 1.2, or something, but we would have to revert the config changes, and I just don't have the energy. Feel free to volunteer, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could look at cc36507 as a potential 1.2 release. I'll try it out in my own codebase at some point and see what happens.

*
* @param string $propertyName Supports only first|last|take
* Returns a blank Relation and fires no queries. Any subsequent call to find(), to_a(), last(), first() returns []
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's there, but put in doc block too now

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you want one of these:

* @returns Relation<TModel>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bump.

$this->options['select'] = array_merge((array) $this->options['select'], (array) $arg);
$columns = array_merge((array) $this->options['select'], $columns);

return $this->reselect($columns);
Copy link
Contributor

@shmax shmax Sep 16, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a bit overly busy to keep crunching the values down on the fly. My instinct would be to just collect information as-is, and do any processing at the end.

FWIW, that seems to be how Rails does it, as well:

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean here. The performance hit one way or another for manipulating short arrays in memory pales in comparison to the actual database hit. So, if performance is concern here, this is not likely going to make a visible difference when using this library. Turning SQL statements would make a much more important improvement in speed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not saying you can't collapse the SELECT statements, I'm saying you should do it in the same place the rest of this kind of work happens, in Table::options_to_sql. Then it's all in one place, and people working with the debugger will be less likely to fall victim to the principle of least surprise. And no, it probably won't amount to much of a performance savings, but that's not exactly a reason not to do it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you're still not convinced we don't have to let it hold up this PR, but be warned that I will likely move things around soon after.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm gone for the weekend, so please feel free to do that. I'm glad we could get this merged before I leave.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #77

$this->expectException(UndefinedPropertyException::class);
$author = Author::select('name')->reselect('author_id')->first();
$author->name;
$this->fail('expected ActiveRecord\UndefinedPropertyExecption');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need this line; as a matter of fact, I think they can likely all be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've copied and pasted $this->fail('expected ActiveRecord\UndefinedPropertyExecption'); from other tests, but didn't know what they were there for. Removed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's my fault, I added all those expectException lines, but didn't catch all the fail calls. Thanks much.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for clarifying.

I tracked the other extraneous $this->fail() functions and removed them in the latest commit

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks much.

@shmax shmax merged commit 830ecc9 into php-activerecord:master Sep 16, 2023
@shmax
Copy link
Contributor

shmax commented Sep 16, 2023

Awesome, thanks!

@ipundit ipundit deleted the RelationMagicFunctionsAndSelectFix branch September 18, 2023 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants