Laravel 5.2 - modify a model's builder - php

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!

Related

Laravel collection: pluck before serialization

I have a laravel Model with a one to many relationship which the user can edit via a multiple select tag.
Before exporting the model as a JSON, I use the "pluck" method to get an array of related IDs instead of an array of models, so that they can be used in the select tag and later be synced again with the "sync" method of Laravel.
However the result of "pluck()" seemingly doesn't persist over serialization. The following code doesn't work -upon serialization, "relationship" becomes again an array of objects-
$model->relationship = $model->relationship->pluck('id');
This one, however, does what it should: somePropertyIHaveJustCameUpWith is an array of IDs
$model->somePropertyIHaveJustCameUpWith = $model->relationship->pluck('id');
1) Why does this happen?
2) I have seen there is this resources way in the documentation, but creating an entire new class for something that could be solved with a single line of code feels like a bit overkill. Isn't there a cleaner way to do that?
I think this is likely a result of the way the model implements toArray().
The you can trace the steps taken, but eventually the relations are read from the $this->relations property on the Model, not from each individual relationship.
So, instead of setting the value of your relation directly like:
$model->relationship = $newValue
... you could try setting it using:
$model->setRelation('relationship',$newValue)
This will update the $model->relations property.
This should allow the toArray() method to get the new value that you set when serializing.
Note that the toJson() method in turn calls the toArray() method when serializing. So either approach will be the same result.

What is the meaning of utility functions in Laravel

Good day to all, I am new to Laravel and the thing that I encountered with and cannot understand is utility methods that are used in the framework, for example, Customer::all() where Customer is model or Customer::find(). So, what is the point of utility methods and why all() and find() are static.
Utility functions in general offer some sort of utility, i.e. functionality which is usually just for convenience and can also be achieved by following a set of steps.
For example Model::find() is functionality that can also be achieved by creating a query builder object and then performing a query e.g.
Model::getQuery()->where('id', '=', $id)->first();
For convenience you can just do Model::find($id) which is shorter and more intuitive.
It is static because it does not make sense for it not to be static. A non-static method requires an instance of the class however in ORMs an instance of a class corresponds to a potential database entry (which either exists or could exist).
Therefore since find is used to retrieve a database entry it makes no sense to require a backing database entry in order to use it.
In short what this means is, if you execute method $object = Model::find(1) you will get back a model which corresponds to database entry with identifier 1. There is a 1 to 1 mapping of the PHP object to the relational object. If make changes to $object and call $object->save() then changes will be persisted in the database (i.e. $object already exists). If on the other hand you do $object = new Model() you will get back a new model which does not currently correspond to a database entry. However calling $object->save() will make that object correspond to a new database entry and it will be updated accordingly (i.e. the new object could exist). If a framework required you to make a "dummy" object just to access some helper methods there's always a chance that (either by omission or through unknown side-effects) save() gets called on that dummy object and you end up filling up your database with what is essentially junk.
The takeaway from this is that in an ORM it does not make sense to create a model instance if you don't intend to store it in the database. Laravel does not strictly obey this rule, but in general it does, and you should too.
The same applies to all which gets all database entries. It doesn't make sense to get all database entries by requiring to first get one entry.
Same applies to getQuery used above which returns a query builder instance. Note that most people don't actually use getQuery because it runs implicitly when someone uses something like e.g. Model::where(..) so technically Model::where() is also a utility. I mention this because when you see something like Model::where('id', $id)->where('name', $name) the 2nd where is after a -> which implies it's on an instance rather than static, however that instance is actually a query builder instance and not a model instance.
When you do User::find() , you are actually asking for a new instance, either an instance of Collection or a QueryBuilder.
Illuminate\Database\Eloquent\Model : https://github.com/laravel/framework/blob/master/src/Illuminate/Database/Eloquent/Model.php
As a side note, you'll also see another way of using static methods in Laravel e.g. Input::get(). These are called Facades.
Facades provide a "static" interface to classes that are available in the application's IoC container ... Laravel "facades" serve as "static proxies" to underlying classes in the IoC container, providing the benefit of a terse, expressive syntax while maintaining more testability and flexibility than traditional static methods.
When a user references any static method on the ... facade, Laravel resolves the cache binding from the IoC container and runs the requested method (in this case, get) against that object.
You can read more about Laravel Facades at: http://laravel.com/docs/facades
Laravel includes a variety of global "helper" PHP functions. Many of
these functions are used by the framework itself; however, you are
free to use them in your own applications if you find them convenient.
So, basically, helpers in Laravel are built-in utility functions that you can call from anywhere within your application. If they hadn't been provided by the core framework, you might have ended up developing your own helper classes.
Although the core provides a variety of helpers already, there’s always a chance that you’ll need your own and would like to develop one so you don’t have to repeat the same code here and there, thus enforcing better maintainability.
all() find() where() etc.. these methods are not utility methods, they are orm methods and coming from Query Builder.
They are static so they provide you using them without creating an instance
Those methods are part of the Eloquent system and help you to retrieve records from your database.
The all method gets all of the records for the given model from the database and returns a Collection of instances of that model. If you were to do User::all(), a collection of every single user in your database would be returned as a Collection of User objects.
$users = User::all(); // Returns a Collection of User objects
foreach ($users as $user) {
// Here, each $user will be an instance of User
}
The find method is used to find a model in the database by its primary key. This method actually returns a single model instance if it finds a record matching the given primary key, or null if it doesn't find anything.
$user = User::find(1); // Find a User with the primary key 1, or null
Those methods aren't actually static, though you can use them as if they were. The find method is a non-static method on the Illuminate\Database\Eloquent\Builder object and the all method is a utility method on Illuminate\Database\Eloquent\Model which eventually calls the Builder object's get method.
When you call User::find(1), Laravel will create a new query or a Builder object and call the find method on the Builder which will return you a new User object, or null. Similarly, when calling User::all(), Laravel will create a new query or Builder object and call the get() method which will return your collection of models.
The Illuminate\Database\Eloquent\Model class uses the __callStatic magic method to redirect static method calls for non-existent static methods to the Builder object. That's how you're able to call them in a static context:
public static function __callStatic($method, $parameters)
{
return (new static)->$method(...$parameters);
}
So you could effectively do this to achieve the same results:
$user = (new User)->find(1);
$allUsers = (new User)->get();

Should I do data insert for a model inside the controller or inside the model?

As my question title states I would like to know all the best practices and pros/cons of following the "Laravel way" of inserting the data in the db.
Is it a bad practice using the Model::create([...]) for inserting a new row in the database which is related to that controller's model?
Is creating a separate function inside the model itself a much better practice?
If so, why?
It depends on size of your project you can use your models directly in your controller and use the fluent interface of Eloquent to do your logic.
There is nothing wrong that you call a model in a controller to get data from database.
If there is some complicated query you can create method in your model and than call that method in controller ...
In laravel you can use insert in controller , view(blade) or even in route .
But recommended is on your controller. This help to make your code clean
You can take a look for resource controller. It has already predefined logic like store() method where you can put some Model::create(), as you said.
That's not bad if you also create a method inside model and call it from your controller.
I think, that create() method for straght storing some data from request (from some form on front-end, as an example) is enough. But if you want/need to make more than straight storing model, a separate method inside model might take place. Apart to avoid controller's method overgrowth and keep your code pretty.
The answer really depends on, so maybe you will shed some light on the logic you want to implement.

How to fill a custom object via the repository pattern and Laravel's Eloquent

Okay, so here's the deal. I'm working with a custom CMS, and I'd like for the code to be as optimized as possible. I've been reading/watching tuts/etc. like crazy about the repository pattern in general as well as specifically using it with Laravel's Eloquent. There are probably some really dumb questions/thoughts in here, but bear with me. :-) Sometimes there's no easy way to ask about terminology/best practices without looking silly.
As with many things, there are million ways I could "make it work"; my dilemma is essentially a matter of "best practice."
General Scenario/Question
Let's assume I am trying to get a Page for my CMS from the database. From what I can understand the typical way to set up the repository pattern with Eloquent is to have the following files:
Page.php -- the Eloquent Model
PageRepositoryInterface.php -- the "contract" for what should be in Page repo's
EloquentPageRepository.php -- the Page repository that can grab data via Eloquent
Easy enough. So I might use it this way. Assuming I have a getPageById method in EloquentPageRepository.php, I could just do something like this from my controller:
$page = $this->repo->getPageById();
Now my question arises: what type of data should getPageById() return? Some people recommend setting it up to return an Eloquent collection. Others say just a plain array or generic object.
Ideally I feel like my scenario would best lend itself to having EloquentPageRepository grab the data from Eloquent and actually return an instance of a custom Page class that I have. For example, something along the lines of this:
<?php namespace Acme\Repositories\EloquentPageRepository;
use Acme\...\PageObject as PageObject; // Better name than PageObject?
//...
class EloquentPageRepository implements PageRepositoryInterface {
// Omitting constructor, etc.
public function getPageById($id)
{
// Grab the row via Eloquent (obviously not stored in Page:: at
// this point. I'm just using it here for clarity and time's sake.
$page = Page::find($id);
// Now we have an Eloquent collection stored in $page, but I'd
// like to return the data inside an instance of my custom class.
$pageObj = new PageObject($page->title, $page->body);
return $pageObj;
}
}
To me, this seems good because it gives a consistent delivery format from repo to repo. It also allows me to perform some constructor logic on my pageObject. Finally, it allows me to have some custom methods on the PageObject (that are repository-agnostic).
It's similar to a collection, but I don't think it's exactly that. It's basically just an instance of a class that I'm immediately populating with my database info.
My questions, listed:
Is it considered bad practice to use a repo to stuff eloquent data into a specific object and return it?
I don't want to call my class "PageObject," because that's just lame. I'd way rather call it something like "PageCollection," except for the fact that it's not actually a collection. Is there actually a name for the way that I'm using this class? It's not a collection, it's a ...? I have no idea about this, I'm just searching for any input you have.
It whole depends on what you expect from the repository pattern. Are you using the repository pattern because in the future you're going to swith of data layer and needs a new repository. If you're using Eloquent as long as your cms live then you can return an eloquent object. If you want it very flexible then make a new page object(PageComposer as mentioned in the comments). This is one of the strengts of the repository pattern so I suggest you make a PageComposer class which you instantiate and return by the repository.
Normally you can call it Page because its a page and it ships some information of a page. But that name you've already give to the Eloquent model. You can consider changing the eloquent models name and call your return object Page.

How to do an object sort in Laravel 4, and is it more efficient to do it in MySQL?

I just followed this todo list tutorial and I have a few questions.
I would like to sort the objects by a key in Laravel and I just cannot figure this out.
Where should I implement this? In Eloquent? In routes? In controllers? Slightly confused about this. I have found a few links going over the sort method using the usort wrapper but I have been unable to implement an object sort (todo list item by a key) successfully.
Would it be more efficient to SQL sort the items? Using something like this:
$sql = "SELECT * FROM `items` ORDER BY `items`.`time` ASC LIMIT 0, 30 ";
Where would I implement this? I feel like the logical place is in my item model. But I'm not sure.
I can post any relevant code, I am just not sure what to post yet, so feel free to ask. And I don't want to spam this post with unnecessary code.
You should perform sorting in your query unless you have other reason, in case of Laravel using Eloquent you may do something like this:
$items = Item::orderBy('id')->get();
Basically, you may put this in your controller, for example:
class UserController extends BaseController {
public function getAllUsers()
{
$users = User::orderBy('username')->get();
return View::make('users.index')->with('users', $users);
}
}
To make query into users table using Eloquent you have to create a User model like this:
class User extends Eloquent {
protected $table = 'users';
}
You can also use this for same thing using the Query Builder even without a User model:
public function getAllUsers()
{
$users = DB::table('users')->orderBy('username')->get();
return View::make('users.index')->with('users', $users);
}
This way, you will be able to query your User model from your controller and then pass the query result to the view where you may show them. This is just a basic implementation but there are other ways using repository classes and known as best practice in a Laravel application. This is a broad topic but you may check this article.
If you have a route like this:
Route::get('users/all', 'UserController#getAllUsers');
Then the getAllUsers() method of the class declared above will be invoked to show all users. Also read the documentation, it's the best place for any reference of Laravel framework.
Routes are for routing so not there. Controllers are for business logic so it is a good place to process the query results. In your case I would simply create a route for a controller method and in that function, I would order with Eloquent's orderBy function, like presented in this question:
$posts = Post::orderBy('id', 'DESC')->get();
It's unnecesary to write SQL queries for something as simple as this, especially if you want to write raw queries. The reason why we use the Eloquent ORM is that we don't want to write raw queries.
You should leave the sorting to the database. Actually, you should leave all database related task to the database, it is optimized for that, and Laravel will use these features so use the framework's solutions.

Categories