Assume we have a model name "Article" in Laravel and want to make query for retrieving latest articles, so one way is to define a method in the "Article" model like this:
public function newArticle()
{
return static::where('created_at', '>', Carbon::subMonths(1));
}
The question is, why we should use
static::
in the above code?
Is it possible to use
$this or self::
instead of
"static::" ?
Thanks in advance,
You could but there is not interest because:
where method does not exist statically on Illuminate\Database\Eloquent\Model class, so it calls __callStatic magic method which delegates the call to an instance
/**
* Handle dynamic static method calls into the method.
*
* #param string $method
* #param array $parameters
* #return mixed
*/
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
It calls where method on instance but it does not exist either, so it calls __call magic method which delegates it an Illuminate\Database\Eloquent\Builder instance.
/**
* Handle dynamic method calls into the model.
*
* #param string $method
* #param array $parameters
* #return mixed
*/
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}
return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}
/**
* Get a new query builder for the model's table.
*
* #return \Illuminate\Database\Eloquent\Builder
*/
public function newQuery()
{
return $this->registerGlobalScopes($this->newQueryWithoutScopes());
}
Related
Is there a way to receive an instance of \Illuminate\Database\Eloquent\Builder instead of \Illuminate\Database\Query\Builder in the handleBuilder method when creating a custom ArgBuilderDirective?
See this example:
<?php
namespace Nuwave\Lighthouse\Schema\Directives;
use Nuwave\Lighthouse\Support\Contracts\ArgBuilderDirective;
use Nuwave\Lighthouse\Support\Contracts\DefinedDirective;
class EqDirective extends BaseDirective implements ArgBuilderDirective, DefinedDirective
{
/**
* Name of the directive.
*
* #return string
*/
public function name(): string
{
return 'eq';
}
/**
* Apply a "WHERE = $value" clause.
*
* #param \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder $builder
* #param mixed $value
* #return \Illuminate\Database\Query\Builder|\Illuminate\Database\Eloquent\Builder
*/
public function handleBuilder($builder, $value)
{
// $builder is an instance of \Illuminate\Database\Query\Builder here.
// Is it possible to receive an instance of Illuminate\Database\Eloquent\Builder instead?
return $builder->where(
$this->directiveArgValue('key', $this->nodeName()),
$value
);
}
}
We frequently use where method with our models but it's not defined in Base Model class so how laravel is doing this magic?
e.g MyModel::where('id, 2)->get();
The above where will definitely fetch the record having id equals 2 but where & how is this happening! I traced back to the Base Model in Laravel but didn't found where method!
Some of this magic resides behind a Facade Pattern https://www.tutorialspoint.com/design_pattern/facade_pattern.htm
But in Laravel case, they use Static methods in their Facades (\Illuminate\Support\Facades namespace), and create instance automatically to call those methods from instance, so you don't have to instantiate the Class yourself to start using the methods. For example when you use DB::, Cache::, Str:: to call static method.
However for Eloquent, Laravel uses this approach more internally without exposing the Model to \Illuminate\Support\Facades namespace, by configuring Eloquent Model to instantiate automatically the Model and creates\Illuminate\Database\Eloquent\Builder Eloquent Builder and forward those static methods call to the Eloquent Builder instance or to Query Builder one.
A model extends \Illuminate\Database\Eloquent\Model which has a special way to call any method statically at runtime.
/**
* Handle dynamic static method calls into the method.
*
* #param string $method
* #param array $parameters
* #return mixed
*/
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
So at a certain point, the Builder classes an Eloquent use a Trait
use Illuminate\Support\Traits\ForwardsCalls;
to forward call to each others so that a Model can forward its static method to method from the created instance of Eloquent builder. So if model does not have where method, it will forward it to the where method of the newly created nstance of Eloquent Builder.
From a look of Facade Pattern, here is how Laravel calls a method from an instance but in a static way :
// \Illuminate\Support\Facades\Facade.php
/**
* Handle dynamic, static calls to the object.
*
* #param string $method
* #param array $args
* #return mixed
*
* #throws \RuntimeException
*/
public static function __callStatic($method, $args)
{
$instance = static::getFacadeRoot();
if (! $instance) {
throw new RuntimeException('A facade root has not been set.');
}
return $instance->$method(...$args);
}
So calling User::where() creates an instance of \Illuminate\Database\Eloquent\Builder and forward method call to that instance, here is the method :
// Illuminate\Database\Eloquent\Builder.php
/**
* Add a basic where clause to the query.
*
* #param string|array|\Closure $column
* #param mixed $operator
* #param mixed $value
* #param string $boolean
* #return $this
*/
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure) {
$column($query = $this->model->newModelQuery());
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
} else {
$this->query->where(...func_get_args());
}
return $this;
}
You can find the method here . framework/src/Illuminate/Database/Eloquent/Builder.php
public function where($column, $operator = null, $value = null, $boolean = 'and')
{
if ($column instanceof Closure) {
$column($query = $this->model->newModelQuery());
$this->query->addNestedWhereQuery($query->getQuery(), $boolean);
} else {
$this->query->where(...func_get_args());
}
return $this;
}
Some more info about it
I'd like to hook a model event to perform a task after the model has been deleted. I've added the following code to my model:
protected static function boot()
{
parent::boot();
static::deleted( 'static::removeStorageAllocation' );
}
Rather than put the logic I want to run inside a closure in the boot function, which seems a pretty ugly spot for it, I noticed in the method signature it supposedly takes "\Closure|string $callback" is there a way I can specify a function name like I've tried to do above? I can't seem to come up with anything that works. I've tried lots of combinations:
'self::removeStorageAllocation'
'static::removeStorageAllocation'
'\App\MyModel::removeStorageAllocation'
I know I can probably just specify a closure which calls my function, but I'm wondering what the string form of $callback is for?
You could just pass an anonymous function:
static::deleted(function() {
static::removeStorageAllocation();
});
To know the string representation of $callback, you could look at the source of deleted:
/**
* Register a deleted model event with the dispatcher.
*
* #param \Closure|string $callback
* #param int $priority
* #return void
*/
public static function deleted($callback, $priority = 0)
{
static::registerModelEvent('deleted', $callback, $priority);
}
You'll see it is registering an event listener:
/**
* Register a model event with the dispatcher.
*
* #param string $event
* #param \Closure|string $callback
* #param int $priority
* #return void
*/
protected static function registerModelEvent($event, $callback, $priority = 0)
{
if (isset(static::$dispatcher))
{
$name = get_called_class();
static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback, $priority);
}
}
Therefore, $callback is used eventually as a listener. A string representation would most likely be the name of a listener class, not a method.
Create a protected or public static function on your model (private will not work):
protected static function myStaticCallback($model)
{
// Your code
}
Then add a boot method to your model, using an array for the callback [class, function]:
protected static function boot()
{
parent::boot();
static::creating(['MyModel', 'myStaticCallback']);
}
So I have a trait like as below:
trait RepositoryTrait
{
/**
* Find a model by its primary key.
*
* #param int $id
* #param array $columns
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function find($id, array $columns = ['*'])
{
return $this->query()->find($id, $columns);
}
}
and the interface:
interface Repository
{
/**
* Find a model by its primary key.
*
* #param int $id
* #param array $columns
* #return \Illuminate\Database\Eloquent\Model|null
*/
public function find($id, array $columns = ['*']);
}
and I have repository class like:
class FixedAssetRepository implements Repository
{
use RepositoryTrait;
/**
* FixedAsset model.
*
* #var FixedAsset
*/
protected $model;
/**
* Repository constructor.
*
* #param FixedAsset $fixedAsset
*/
public function __construct(FixedAsset $fixedAsset)
{
$this->model = $fixedAsset;
}
}
and what I'm looking for is to define somehow that method find (in trait or interface) is type of FixedAsset - but this should work always for each new Repository class that I'll create (that implements Repository interface).
I need it for type hinting in PhpStorm 10.0.4
Is it possible somehow?
So the result should be that when I call somewhere I.e.:
$fixedAsset = $this->fixedAssetRepositry->find($id);
PhpStorm will know that $fixedAsset is a object of a class FixedAsset not just \Illuminate\Database\Eloquent\Model (now is working like that).
So I need something that like in interface (or trait?):
/**
* Find a model by its primary key.
*
* #param int $id
* #param array $columns
* #return $this->model
*/
public function find($id, array $columns = ['*']);
but of course #return $this->model doesn't work.
I'll be appreciated for any suggestions about that.
The only solution that I can think of right now would be using #method in PHPDoc comment for that FixedAssetRepository class and "redeclare" that find() method with correct signature (return type). Since it's a PHPDoc solution it will not have any effect on PHP code during runtime.
Sample code:
/**
* #method FixedAsset find(int $id, array $columns) Find a model by its primary key
*/
class FixedAssetRepository implements Repository
{
...
More on the #method tag -- https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md#711-method
i wonder how to use callback validation using MY model - made by Jamie Rumbelow with Modular extension - wiredesignz
i have create MY_Form_validation and modified the run function like this:
class MY_Form_validation extends CI_Form_validation {
/**
* Stores the CodeIgniter core object.
*
* #access public
*
* #var object
*/
public $CI;
/**
* Constructor
*
* #return void
*/
function __construct($config = array())
{
// Merged super-global $_FILES to $_POST to allow for better file validation inside of Form_validation library
$_POST = (isset($_FILES) && is_array($_FILES) && count($_FILES) > 0) ? array_merge($_POST,$_FILES) : $_POST;
parent::__construct($config);
}
/**
* Performs the actual form validation
*
* #access public
*
* #param string $module Name of the module
* #param string $group Name of the group array containing the rules
*
* #return bool Success or Failure
*/
public function run($module='', $group='')
{
(is_object($module)) AND $this->CI =& $module;
return parent::run($group);
}
it work perfectly if i run form validation with callback in controller, but i wonder how to run in inside a model, it always skip the callback
thank you for your time, and your answer :)