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

Trait collision: Conflict with other traits overriding newCollection method #287

Open
yusrashahina opened this issue Feb 22, 2025 · 5 comments

Comments

@yusrashahina
Copy link

When using HasRecursiveRelationships alongside traits that override newCollection (e.g., Mediable from plank/mediable), a fatal error occurs due to method collision:
Trait method 'newCollection' will not be applied because it collides with another trait method.

Steps to Reproduce:

composer require plank/mediable staudenmeir/laravel-adjacency-list

Create a model using both traits:

use Plank\Mediable\Mediable;  
use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;  

class Category extends Model {  
    use Mediable, HasRecursiveRelationships;  
} 

Attempt to use the model (e.g., fetch records).

Trait method 'newCollection' has not been applied because there are collisions with other trait methods on App\Models\Category

Temporary Workaround:
A custom collection class can be created to combine both traits’ collection logic, but this requires manual delegation (example below). However, this may not cover all edge cases.

Request:
Could the package provide a way to avoid overriding newCollection or make it compatible with other traits that modify collections? For example:

  • Allow configuration to disable the newCollection override.
  • Use a trait without the newCollection method (e.g., split HasAdjacencyList into subtraits).
  • Use composition instead of inheritance for collections.

Example Workaround Code:

// Custom collection  
class HybridCollection extends MediableCollection {  
    use \Staudenmeir\LaravelAdjacencyList\Eloquent\Concerns\HasCollection;  
}  

// Model  
class Category extends Model {  
    use Mediable, HasRecursiveRelationships;  

    public function newCollection(array $models = []) {  
        return new HybridCollection($models);  
    }  
}  
  • Environment:
  • Laravel: 11.x
  • PHP: 8.2
  • Package version: staudenmeir/laravel-adjacency-list:^1.7

Thank you for this excellent package! Any guidance or updates to resolve this would be appreciated.

@staudenmeir
Copy link
Owner

Hi @yusrashahina,

Are you using the collection's methods toTree() or loadTreeRelationships()? If not, you can use newCollection() from the other package:

use Plank\Mediable\Mediable;  
use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships;  

class Category extends Model {  
    use Mediable, HasRecursiveRelationships {
        Mediable::newCollection insteadof HasRecursiveRelationships;
    }
} 

@yusrashahina
Copy link
Author

Hi @staudenmeir,

I do use toTree() and other adjacency list features that depend on the package’s custom collection. Using Mediable::newCollection insteadof HasRecursiveRelationships breaks these features (e.g., toTree() is unavailable).

Is there a way to retain both MediableCollection and adjacency list functionality?

Our current workaround (custom HybridCollection) works partially but may miss edge cases. Any guidance would be appreciated!

@staudenmeir
Copy link
Owner

In this case, you do need to merge both Collection classes into a single one.

Our current workaround (custom HybridCollection) works partially but may miss edge cases. Any guidance would be appreciated!

What does your current HybridCollection class look like?

@yusrashahina
Copy link
Author

Here’s our current HybridCollection:

namespace App\Collections;  

use Plank\Mediable\MediableCollection;  
use Staudenmeir\LaravelAdjacencyList\Eloquent\Collection as AdjacencyCollection;  

class HybridCollection extends MediableCollection  
{  
    public function __construct(array $models = [])  
    {  
        parent::__construct($models);  
        // Proxy adjacency list methods  
        $this->adjacencyProxy = new AdjacencyCollection($models);  
    }  

    public function __call($method, $parameters)  
    {  
        if (method_exists($this->adjacencyProxy, $method)) {  
            return $this->adjacencyProxy->{$method}(...$parameters);  
        }  

        return parent::__call($method, $parameters);  
    }  

    // Basic adjacency list method (example)  
    public function toTree()  
    {  
        return $this->adjacencyProxy->toTree();  
    }  
}

Methods like map() or filter() return a MediableCollection instead of HybridCollection, breaking adjacency list functionality in subsequent calls. Is there a way to ensure all collection methods return the merged collection type?

@staudenmeir
Copy link
Owner

staudenmeir commented Mar 4, 2025

Have you tried this approach?

namespace App\Collections;  

use Plank\Mediable\MediableCollection;

class HybridCollection extends MediableCollection  
{
    public function toTree()  
    {  
        // TODO: Copy code from Staudenmeir\LaravelAdjacencyList\Eloquent\Collection
    }  
}

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

No branches or pull requests

2 participants