With, where, hasMany etc. methods of Eloquent class are not static.
However we call these functions like that:
// Post is a child of Model class.
Post::where(...); // we don't use New keyword.
So, does Laravel Framework initiate all Model instances before we call their methods?
In the Eloquent's Model class, you have the below function which handles the static method calls dynamically.
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
As you can see, it creates an instance of the Model class on which a non static method is invoked statically, then invokes that method on the instance.
Related
I'm watching the Laracasts: Laravel 5.4 from Scratch series, and have come across the concept of query scopes.
In the video, we set up a class like this:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Task extends Model
{
public function scopeIncomplete($query)
{
return $query->where('completed', 0);
}
}
My first question:
Why is a method like: public function scopeIncomplete($query) called like this: App\Task::incomplete() with the :: operator?
Isn't that a non-static method being called statically?
My second question:
Where does $query come from?
From what I can understand this is the "existing" query, but it is called like this: App\Task::incomplete()->where('id', '>', 1)->get();
So I'm not really sure where this variable is coming from.
Maybe it's explained more in depth later but I can't wrap my head around how this works.
This is pretty broad since it is covering two large topics on Eloquent.
The first refers to Eloquent's use of the facade pattern to pass static calls to non-static methods.
The facade pattern utilizes the IoC container to pass a method call to a bound, or new, instance of a class.
The facade pattern makes use of overloading and the magic method __callStatic.
The second question refers to Eloquent's overloading that passes the unregistered method calls to a query builder object (specifically \Illuminate\Database\Eloquent\Builder). This is what allows for where() and various other Query Builder methods to be called on the Eloquent model itself. Inside the Model class, you can see:
/**
* 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->newQuery()->$method(...$parameters);
}
Since where() or incomplete() are not defined in Model, they'll be passed to the Builder class, which is returned by Model::newQuery().
Inside the Builder __call method, you have:
if (method_exists($this->model, $scope = 'scope'.ucfirst($method))) {
return $this->callScope([$this->model, $scope], $parameters);
}
So this is checking for the scopes defined inside the model and passing the $parameters which includes this Builder instance.
So in summary, the call for Model::incomplete() will go:
Model __call() -> Builder __call() -> Model scopeIncomplete(Builder $builder)
I am learning how some features are being implemented in laravel, because i want to understand some software design techniques and principles.
i understand that when a static method call such as "App\User::find(1) or App\User::whereId(1)" are made on an eloquent model that the abstract model class implements a magic method "__callStatic" like so :
/**
* Handle dynamic static method calls into the method.
*
* #param string $method
* #param array $parameters
* #return mixed
*/
public static function __callStatic($method, $parameters)
{
$instance = new static;
return call_user_func_array([$instance, $method], $parameters);
}
I also understand that this line '$instance = new static;' makes an instance of whatever eloquent model in which the static call was made e.g App\User.
However i don't completely understand whats going on in the next line "call_user_func_array([$instance, $method], $parameters);".
From what i have learnt so far the function call_user_func_array() is suppose to call the $method(e.g find($parameters) ) on the eloquent model instance (e.g App\user).
But i dont understand why that is not the case,and i discovered that method do not exist on the eloquent model. i tried calling a none existing method like "blah()" :
App\User::blah();
But i get the exception "BadMethodCallException with message 'Call to undefined method Illuminate\Database\Query\Builder::fisd()'".
Please how is the class "Builder" entering the scene ?
why is the exception not ""BadMethodCallException with message 'Call to undefined method Illuminate\Database\Eloquent\Model::fisd()'"" ?
i cannot see the model class extending Builder Class.
The reason this happens is because it then triggers the __call method on the class which will try and call the method using newQuery() which returns an instance of Builder.
__call, like __callStatic, is called when you try and call an inaccessible (or nonexistent) method on a class.
So, when you try and call find() statically it will get caught by __callStatic because it doesn't exist which in turn then tries calling find() on the new instance which again doesn't exist so it gets caught by __call and it finally tries to call that method on Builder.
Hope this helps!
Normally Eloquent model is used as following:
class Article extends Eloquent
{
// Eloquent Article implementation
}
class MyController extends BaseController
{
public function getIndex()
{
$articles = Article::all(); // call static method
return View::make('articles.index')->with('articles', $articles);
}
}
But when restructing use Dependency Injection, it looks like that:
interface IArticleRepository
{
public function all();
}
class EloquentArticleRepository implements IArticleRepository
{
public function __construct(Eloquent $article)
{
$this->article = $article;
}
public function all()
{
return $this->article->all(); // call instance method
}
}
So why we can call the static method Article::all() in form of instance method $this->article->all()?
P/S: Sorry for my bad English.
Good question.
Laravel utilize the Facade design pattern. when you call Article::all(), a lot of things happened behind the screen. First, PHP try to call the static method if it fails php immediately call a magic method _callStatic. then Laravel cleverly capture the static call and create instance of the original class.
From Laravel doc:
Facades provide a "static" interface to classes that are available in the application's IoC container. Laravel ships with many facades, and you have probably been using them without even knowing it!
More info:
http://laravel.com/docs/facades
http://usman.it/laravel-4-uses-static-not-true/
I am using an ORM class - each table in the DB is represented using a subclass of the ORM class.
I am using PHP interfaces, and I wish to specify which methods (db fields) are required in some of my ORM subclasses. Adding a function to an interface requires the method to be explicitly declared in the class. However, these methods rely on magic methods for the actual functionality as the DB structure is unknown to the ORM before run time.
What I imagined doing was creating functions for each, which would return a result from the parent class.
Consider:
class ORM
{
// Library code here. Can't change this.
public function __call($name, $arguments)
{
return call_user_func_array(array($this, $method), $arguments);
}
}
interface MyTableInterface
{
public function myDbField();
}
class MyTable extends ORM implements MyTableInterface
{
public function myDbField()
{
return parent::myDbField();
}
}
With this code, when I call parent::myDbField() from the MyTable class, it correctly moves to the ORM class and uses the __call magic method. Once here, $this equals MyTable and it calls the original function from the MyTable class instead of initiating it's own logic to pull info from the DB.
How can I avoid this recursion?
One way to do it could be passing $orm instance as a dep (constructor, setter or whatever logic). Ex:
class MyTable {
protected $orm;
public function __construct(Orm $orm)
{
$this->orm = $orm;
}
public function myDbField()
{
return $this->orm->myDbField();
}
}
This way, $this inside Orm __call refers to Orm instance. Perhaps, this could be a scenario to use adapter pattern?
call method is only invoked when there is no method found, in your example it is not possible because the method is found in the subclass
If I give the same name to a function in the Model of Codeigniter, that function gets called automatically when I load the model.
//controller
$this->load->model('my_model');
//model
class My_model extends CI_model {
function my_model {
}
}
In this case I don't have to call my model function like this
$this->my_model->my_model();
because loading the model calls the function as well.
Can somebody explain this behaviour? I haven't found anything in docs regarding this.
This is a common concept in Object-Orientated programming. The function is acting as a Constructor. The constructor is called when an instance of the object is created.
In PHP using the __construct() method is the advised way to declare a constructor for the class. However, in PHP 4, a constructor was declared using the class name, so:
For backwards compatibility, if PHP 5 cannot find a __construct()
function for a given class, and the class did not inherit one from a
parent class, it will search for the old-style constructor function,
by the name of the class.
In CodeIgniter, a model is a class. As you don't have a __construct() method in your class, PHP is treating your my_model function as the constructor for the class (as it is the same as the class name).
You may want to the following method to your model. This will stop my_model being treating as a constructor.
function __construct()
{
// Call the Model constructor
parent::__construct();
}
I'd personally avoid calling a method the same name as the class in PHP, as it can lead to this confusion! PHP's docs have some useful information on constructors.