Larastan: Support for custom Eloquent Collection

Created on 1 Feb 2020  路  9Comments  路  Source: nunomaduro/larastan

Users can override the Eloquent's newCollection method to return a custom Eloquent collection.

We should add support for this.

It'll affect the collection returned by methods like find, all etc. As well as collections returned by relationships.

core-package enhancement good first issue

All 9 comments

Because it's flagged as good first issue - could you point out where the custom collection stuff should hook in/get placed?

Hi,

Thanks for taking an interest.

For starters, everywhere we use Collection::class in the code, needs to be changed with the custom collection class name. Figuring out if model uses custom collection and getting the custom collection class name can be extracted to a helper class/method. Like in the BuilderHelper with determineBuilderType

I think that we even doesn't have to check if the method is overridden because by default the newCollection method returns the default collection. So it could be simpler to just do get_class($model->newCollection()) all the time!?

I'm not sure when I will find time, but if no one has already started with it I could try it. If I start I will write here - would be great if another dev starting it would also write so we won't do it twice. :)

Yes, that can also work. :+1: But that logic still should be extracted to one place.

When calling a method on that custom collection class that has been returned by newCollection(), I still got an error:

Call to an undefined method Illuminate\Database\Eloquent\Collection::collectionMethod()

The solution was to define a "property-read" for the HasMany-property:

@property-read \App\AccountCollection $accounts

This was with phpstan 0.12.57, larastan v0.6.10


Full code:

AccountCollection.php:

<?php
namespace App;
use Illuminate\Database\Eloquent\Collection;

/**
 * @template TModel
 * @extends Collection<TModel>
 */
class AccountCollection extends Collection
{
    function collectionMethod(): void
    {
    }
}

Account.php:

<?php
namespace App;

class Account extends \Illuminate\Database\Eloquent\Model
{
    /**
     * @param array<int, Account> $models
     *
     * @return AccountCollection<Account>
     */
    public function newCollection(array $models = []): AccountCollection
    {
        return new AccountCollection($models);
    }
}

User.php:

<?php
namespace App;
use Illuminate\Database\Eloquent\Relations\HasMany;

/**
 * @property-read \App\AccountCollection $accounts 馃憟 This is still required
 */
class User extends \Illuminate\Foundation\Auth\User
{
    public function accounts(): HasMany
    {
        return $this->hasMany(Account::class);
    }

    public function testCollectionMethodCall()
    {
        $this->accounts->collectionMethod();
    }
}

Very nice tutorial.
Thank you!

@cweiske Can you show the code that produces that error?

Edit: Sorry it's this piece of code I guess:

public function testCollectionMethodCall()
{
    $this->accounts->collectionMethod();
}

This should work without adding the property-read I'll look into it.

Yes, that's the one :)

@cweiske It's fixed by https://github.com/nunomaduro/larastan/pull/719 You can try it on the master branch

Was this page helpful?
0 / 5 - 0 ratings