I am trying to understand how could an Eloquent model also be a query builder. I could not see any polymorphic relationship between Illuminate\Database\Eloquent\Model class and Illuminate\Database\Query\Builder class in the source code. There is no inheritance or interface implementation between these two classes.
So, could anyone please explain how Laravel made it possible for an eloquent model to be a query builder? Thanks in advance.
Edit: I am asking this question because I don't understand how I could call the methods in Builder class through my models, e.g. ModelObject::orderBy('created_at', 'asc')->get(), where orderBy() is a method in Builder class.
There is a query() function on the Eloquent base model which creates a new query builder instance when being called (with the table of the model being already set for the builder). You can find the function here.
Additionally to that, the magic __call() and __callStatic() functions create a new query builder and delegate the method call to this builder whenever a function is called that is not part of the Eloquent model and which they don't have an implicit call for (like increment(), decrement()). You can find the code for this here.
So to sum it up: it's magic. The magic functions.
Related
Which is the best method to use for the CodeIgniter 4 among the Model and the Query Builder class? What are the limitations of the CodeIgniter 4 Model and the Query Builder?
I searched a lot about this question. But I did not get any clear answer. For complex queries currently, I am using the Query Builder class. But I want to know what is the best method among the CodeIgniter 4 Model and the Query Builder class. Your answers are highly appreciated.
It depends on your needs and preferences.
Model class - This is used by extending the base Model class, and you can add your own functions/methods. It is not as flexible as the Query Builder, and it only has pre-defined methods.
Query Builder - Easy to build complex queries. But more room for errors if didn't handle it. Ex SQL injection if you failed to validate it properly.
So, I am using Laravel's built in soft Deletes and I have run into an issue. When I call withTrashed() it returns a query builder object. This would be fine for most cases but after I call this I then want to call a method like filter($this->filters) which is specific to the MODEL and I no longer have access to now that I only have a query builder.
The reverse relationship also does not work as withTrashed() also wants a model where any kind of parsing method I can think of would return the model's query builder.
So I was hoping to find a way to modify a model object with where clauses so I can add my filters and send the model to withTrashed() WITH the filters in place. I am honestly not sure how to do this. Worst case scenario I can have the filter method return the query builder without any global scopes and add the withTrashed() query on to the end manually.
So the end result would be something like this:
$model->filter($this->filters)->withTrashed();
Im not trying to get a giant collection and whittle it down. Even with chunking when you have a few million rows it can get slow really quick, especially when you bring in filtering. Technically, for a one off, I could just add multiple ->where() clauses to the query builder before i call ->get() but that would mean doing custom filtering in every controller's index. So i am looking to abstract it as a method in the model that parses filters sent in and adds them to the model (this is the part im not sure on. Kind of like: github.com/marcelgwerder/laravel-api-handler
Any ideas?
I believe you're looking for query scopes. You can create a new query scope on your model, and then it can be used like any other query builder method.
For example, to create a filter() scope:
class MyModel extends Model {
public function scopeFilter($query, $filters) {
// modify $query with your filters
return $query;
}
}
With that query scope defined, you call it like any other query builder method:
$data = \App\MyModel::withTrashed()->filter(/* your filters */)->get();
You can read more about query scopes here.
Filter is a method for a collection, as you can see here https://laravel.com/docs/5.2/collections#method-filter
To make that returns a collection you should use a method like get(), as you can see here https://laravel.com/docs/5.2/eloquent#querying-soft-deleted-models
$yourVar = \App\YourModel::withTrashed()->get();
You will be able to filter in $yourVar after that! Or use where() method to get collection filtered by the query
$yourVar = \App\YourModel::withTrashed()->where('active = 1')->get();
In both, $yourVar will be a Collection of results returned by get() method
Don't know if already has this problem, but i think that i figured out a way to do it. Using Eloquent's Global Scopes (https://laravel.com/docs/5.2/eloquent#global-scopes)
Soft deletes in fact are a global scope.
You will create an implementation for Scope class, then you will create a apply method that will perform the where clause.
Finally in your model you will override the boot method to say that this Model uses this global scope, every query in this Model will be performed in this way.
In the doc link there is a example of how do it and more explanations from Laravel creator.
Hope that this help!
-------- Edit --------
I see now that already has a solution, but maybe this way may be better for you or not, or for other, anyway, just another approach!
As I am not sure, is it possible to create models with DB Class instead of Eloquent? I want to stay away from ORM.
Thanks
Yes of course its possible. You dont need to extend any class to make a model class that encapsulates business logic and consists of methods calling the DB class.
Just create your model inside app/models/MyModel.php like this
class MyModel{
public static function getMyData(){
return DB::table('users')->select('column')->get();
}
}
then you should be fine to call your new class statically:
$data = MyModel::getMyData();
If you wanted to extend the DB class you could, though more likely you would be looking to extend the Database/Builder class to extend functionality but this is a complex topic and I suspect you would have asked a very different question if this was what you were after.
As I final note, I wouldn't steer clear of Eloquent, it's the greatest thing about Laravel amongst a lot of other great things
Just remove the "extends Eloquent" and build the queries using the DB class.
I would like to know how to use Fluent Query Builder in a model? I cannot find any example on how to do it properly. All examples are using Eloquent ORM. From laravel's documentation, to create Eloquent model is by extending Eloquent class :
class User extends Eloquent {}
I understand Eloquent can be great but I am new to laravel right now and all Eloquent is doing is it's confusing me. How would I write the model? so that i can use it in my controller?
I come from codeigniter where I would write
class some_model extends CI_Model {}
and i was able to easily autoload this model into my control and take advantage of it. How is it done with fluent?
The Eloquent ORM extends Fluent, so all the fluent methods are available. Eloquent is just like some syntax sugar. I suggest you get used to it, it will keep your code cleaner.
Heres a example:
// Fluent query builder
DB::table('users')->where('id', '=', 1)->first();
// Eloquent
User::find(1);
// Generated SQL
select * from users where id = 1 limit 1;
Both generate the same SQL, and behind the scenes Eloquent is using Fluent. The main difference is that using Eloquent requires you to have models that extend the Eloquent model.
The important thing to understand is that Eloquent methods are available to you ONLY if you extend the Eloquent class. The fluent query builder methods are always available to you as long as you have specified a correct database for your application.
So why use Eloquent at all?
As in the above example Eloquent and Fluent generates the same SQL, but theres still major differences in the returned result when the response is more complex.
The Fluent query builder will return a "simple" response with just the values, theres no methods available. This is depending on your PDO settings.
Eloquent will do much more for your. You get methods available to you that comes straight from the Eloquent model. Theres also one great benefit here, Eloquent will return a collection that implements has many useful interfaces. This means you can do a lot with the returned data.
Heres some good reads:
Whats eloquent and Fluent?
Eloquent collections
Well, you can just use it like the docs are displaying. Example:
<?php
class Foobar extends Eloquent
{
public static function retrieve($code, $language)
{
return static::where('code', $code)->where('language', $language)->first();
}
}
$foobar = Foobar::retrieve('code', 'EN');
I'm using static::where because this is a static method. (no shit, Sherlock (-: )
You can also just use $this->where() or any other method then where() if the model is initialized. link to query builder docs
I'm just learning Laravel 4 and I was unsure about how Eloquent models work. It is clear to me that they are a good way to interact with the database but can I define constructors and functions on a class that extends Eloquent to use it for more than database interaction?
Yes! A class that extends Eloquent, doesn't make it a class solely used for Eloquent purposes. You've got the realize the concept of class extension. Class extension is there to simply add features to ANY class from another, existing class. In this case Eloquent.
You can take any model you already have, and tomorrow decide that you want to use it with Eloquent and simply extend it. Your original class is still your original class and works the same. Any methods or properties in the original class will ovewrite its parent (Eloquent) if the parent has something with the same name.
In fact, creating other methods in a class that extends Eloquent is the real way to create robust models that do all sorts of cool stuff. I create methods in my User model for example, to calculate how many day's until their birthday. Instead of pulling the birthday column into a controller then using PHP to do the calculation, I just have a method like User::daysUntilBirthday();
If you use a constructer and extend Eloquent, make sure you still fire off Eloquent's constructor as well though.
class MyModel extends Eloquent {
public function __construct($attributes = array(), $exists = false)
{
parent::__construct($attributes, $exists); // This will fire off Eloquent's constructor.
// Your construct code.
}
}