skip to Main Content

I’ve got a Queryable trait which implements the __callStatic method to make a new instance of a Builder and run the called function on it (if it exists). It returns the Builder in order to chain queries (much like Eloquent).

Now I’m trying to implement an interface that needs the find() function but I’ve got the find() function declared in phpDoc on the Quaryable trait (@method static static find(mixed $primaryKeyValue)) and it creates a conflict:

Declaration of ‘Queryable::find(primaryKeyValue: mixed)’ must be compatible with ‘StorageInterface->find(primaryKeyValue: mixed)’, cannot make interface method

In order to fix the conflict, I’m trying to use something like the trait alias (use Queryable { Queryable::find as queryFind; }) to resolve it, but then I get hit with the

Compile Error: An alias was defined for appFrameworkTraitsQueryable::find but this method does not exist

I can’t seem to find a solution for this, could some guru help me out? 🙂 (pretty sure it has to do with somekind of exists check on the class that fails because it is a magic method but I wouldn’t know how to fix that either..)

<?php

namespace appFrameworkTraits;

use appFrameworkDatabaseBuilder;
use appFrameworkDatabasePaginator;

/**
 * @method static int count()
 * @method static static|array<static> first(int $amount = 1)
 * @method static static|array<static> last(int $amount = 1)
 * @method static string getQuery()
 * @method static string getRawQuery()
 * @method static string getPrimaryKey()
 * @method static static find(mixed $primaryKeyValue)
 * @method static array<static> all()
 * @method static array<static> get()
 * @method static Paginator paginate()
 * @method static Builder table(string $tableName)
 * @method static Builder primaryKey(string $primaryKey)
 * @method static Builder perPage(int $perPageAmount)
 * @method static Builder where(string $column, mixed $operatorOrValue = null, mixed $value = null)
 * @method static Builder whereIn(string $column, array $search)
 * @method static Builder whereNotNull(string $column)
 * @method static Builder select(...$columns)
 * @method static Builder with(string $relation, Closure $closure)
 * @method static Builder orderBy(string $column, string $direction = 'desc')
 * @method static Builder limit(int $amount)
 * @method static Builder offset(int $amount)
 */
trait Queryable
{
    public static function __callStatic(string $name, array $arguments)
    {
        $functions = get_class_methods(Builder::class);
        if (in_array($name, $functions)) {
            $builder = new Builder(static::class);
            return $builder->$name(...$arguments);
        }

        $selfFunctions = get_class_methods($calledClass = static::class);
        if (!in_array($name, $selfFunctions)) {
            throw new BadMethodCallException("Static method '{$name}' does not exist on {$calledClass}.");
        }

        return static::$name(...$arguments);
    }

}


class BaseModel extends stdClass implements QueryableInterface, StorableInterface
{
    use Queryable {
        Queryable::find as queryFind;
    }

    public function find(mixed $primaryKeyValue): static
    {
        return self::queryFind($primaryKeyValue);
    }
}

<?php

namespace appStorageMethods;

interface StorableInterface
{
    function getObject(mixed $primaryKeyValue): static;

    function find(mixed $primaryKeyValue): static;

    function save(): static;
}

2

Answers


  1. Trait methods can be "renamed": (actually it’s an alias)

    use Queryable {
        find as MyFind;
    }
    

    Reference

    Simple example:

    <?php
    
    Trait myTrait
    {
        public static function __callStatic ($method, $arguments)
        {
            $className = self::class;
            
            return (new $className ())->$method(...$arguments);
        }
        
        
        private function find()
        {
            echo "naaaaaaaaaaaaan";
            return $this;
        }
    }
    
    
    interface MyInterface
    {
        public function find();
    }   
    
    
    class Test Implements MyInterface
    {
        Use myTrait {
            find as myFind;
        }
        
        public function find()
        {
            echo "nxxxxxxn";
        }
        
    }
    
    
    Test::myFind()->find();
    
    Login or Signup to reply.
  2. Well, you want even more magic. The following code is absolutely not recommended due to debug_backtrace usage which i believe should not be met in the production code when everything goes as it should:

    class QueryBuilder{
        function find( mixed $arg ){
            $this->arg = $arg;
            print_r($this);
            return $this;
        }
    }
    
    interface Findable{
        static function find( mixed $args );
    }
    trait Queryable{
    
        public static function __callStaticWithMoreMagic(mixed $args){
            $method_name = debug_backtrace(2)[0]['function'];
            return (new QueryBuilder())->$method_name( $args );
        }
    
    }
    class SomeModel implements Findable{
        use Queryable {
            Queryable::__callStaticWithMoreMagic as find;
        }
    }
    
    print_r( SomeModel::find('something') );
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search