Well, I've started with Laravel just a few weeks ago, so sorry if I'm repeating something obvious but... here's the thing:
I've got a couple of query scopes in a Photo model:
public function scopeSkipFirst($query)
{
return $query->where('id', '>', 1);
}
public function scopeSearch($query, $search)
{
return $query->where('title', 'LIKE', "%$search%");
}
Now, I want the first one to be executed everytime I make an Eloquent query by that model, like in Photo::all(); and I want the second query scope to be available to any other model.
What's the best practice way to do this? Are both scenarios global scopes? I've been reading a few posts (like this one), but I have no clear ideas about which documentation should I refer (Laravel's 4.2 # Global scopes section; 5.1 Eloquent's # Events; ¿?).
If you want all of your models to have a scopeSearch() method, then it would make sense to move it to a trait and then apply that trait to your models. Something like Searchable:
trait Searchable
{
public function scopeSearch($query, $search)
{
return $query->where($this->getSearchField(), 'LIKE', "%$search%");
}
protected function getSearchField()
{
return 'title';
}
}
I’ve also made the column configurable as not all models may have a title column. For example, when I create an Article model in my applications I’ll have a headline column instead of title.
With the above trait, you can make a model searchable by implementing the trait:
class Photo extends Model
{
use Searchable;
}
You don’t want to make it a global scope. Global scopes are applied to every query. Not every query is going to be a search query, and there also won’t be anything to pass as a search query.
The scopeSkipFirst() method, could be made a global scope if you wanted that to apply any time you queried your Photo model, but I can’t think of a reason why you would want to always skip a particular record. Why have it in the database if you never want to display it?
Related
I've inherited a Laravel 5 project at work and would like to know how I should check for the existence of a related model to avoid null exceptions.
BlockDate model
class BlockDate extends Model {
public function claims()
{
return $this->belongsToMany(User::class);
}
}
User model
class User extends Model {
public function blocks()
{
return $this->belongsToMany(BlockDate::class);
}
}
Pivot table
$table->unsignedInteger('user_id');
$table->foreign('user_id')->references('id')->on('users');
$table->unsignedInteger(block_date_id');
$table->foreign('block_date_id')->references('id')->on(block_dates);
Users can claim a range of dates for vacation requests. However, users may have no claims or dates may not have been claimed. I am currently using
if ($user->blocks->count() > 0) {
$dates = $user->blocks->sortByDesc('created_at');
// more logic here....
}
I do not like using count everywhere, is there a way to incorporate the check like:
// I don't know what hasClaimedDates might be
$dates = $user->hasClaimedDates()->blocks->sortByDesc('created_at');
You can use the actual relationship method instead of the magic accessor:
$sortedDates = $user->blocks()->latest()->get();
This will give you an empty collection if no relations are established, but it will not fail on the sorting.
Note: latest() is an equivalent for orderBy('created_at', 'desc') in this case.
By the way, if you use $user->blocks->count(), it will first load all related models into memory and then count on the relation. If you are going to use the related models afterwards, that is fine. But if you don't and you only count them, this is a waste of resources. In this case $user->blocks()->count() is way more performant as it executes a database query that only returns a single number. Take this into consideration especially where you have a lot of related models.
Laravel offers an optional helper method to guard against nulls:
// will return either a collection or null
$dates = optional($user->blocks)->sortByDesc('created_at');
I'm confused about the best way to get data from DB. I have this controller (Task) that get from Task model the tasks of each customers. What's the best way to get these data?
1° Example
In this example I have a "general" function (getTasksCompany) that join the tables (Task and Companies). The showTasks call this function and then use where clause for get only tasks with customer code = 000001
public function showTasks() {
$tasks = $this->getTasksCompany()->where("company_code", "=", "000001")->get();
dd($tasks);
}
public function getTasksCompany() {
$tasks = Task::join("companies AS c", "c.code", "=", "company_code");
return $tasks;
}
2° Example
In this example I have a "specific" function that get tasks from the code in the passed as argument.
public function showTasks2() {
$tasks = $this->getTasksFromCompany("000001");
dd($tasks);
}
public function getTasksFromCompany($company_code) {
$tasks = Task::join("companies AS c", "c.code", "=", "company_code")->where("company_code", "=", $company_code)->get();
return $tasks;
}
3° Example
In this example I have a "general" function (getTasksCompany) that join the tables (Task and Companies) and I use the scope defined from Task model to filter the tasks.
public function showTasks3() {
$tasks = $this->getTasksCompany()->company("000001")->get();
dd($tasks);
}
public function getTasksCompany() {
$tasks = Task::join("companies AS c", "c.code", "=", "company_code");
return $tasks;
}
public function scopeCompany($query, $company_code) {
return $query->where("company_code", "=", $company_code);
}
My question is, what's the good practice to do? And Why?
Based on my understanding, asking for best practices would attract opinionated answer but generally because you use Laravel, I would try as much as possible to make use of the functionalities it provide.
While I would prefer the third example more than the others because using model scope helps to create and bind query builder from an instance of the model. This would make things easier when you reuse this function.
This means you don't need to statically call any query builder method since they bind to the initial model in the first place.
An example if I would do the above I would simply employ Model Relationship that would handle my joins under the hood:
//Company model
public function scopeShowTask($company_code = "000001")
{
return $this->tasks()->where("company_code", "=", $company_code);
}
public function tasks()
{
return $this->hasMany(Task::class, 'company_code', 'code');
}
Using this method helps to construct your query based on the relationship between Task and Company. In order to understand how this works, you should check out Eloquent Relationship
One great advantage of using this method is that you can easily take advantage of the various method laravel provides when you declare a relationship in your model this way. To see some of them, you can check out Querying relationship
PS: Best practice, no, maybe just a better practice given the situation. This answer is open to an update
I suggest that you study eloquent and query builder thoroughly and that's where the best practice is.
If you use eloquent with query builder properly you won't need another function in order fetch the data that you want.
https://laravel.com/docs/5.5/queries
https://laravel.com/docs/5.5/eloquent
My Association model looks like this (irrelevant code redacted):
class Association extends Model
{
public function members() {
return $this->hasMany('App\Member');
}
}
My Member model looks like this:
class Member extends Model
{
public function scopeActive($query) {
return $query->where('membership_ended_at', Null);
}
public function scopeInactive($query) {
return $query->whereNotNull('membership_ended_at');
}
}
This is what I want to be able to do:
$association = Association::find(49);
$association->members->active()->count();
Now, I'm aware there's a difference between a Query and a Collection. But what I'm basically asking is if there's some kind of similar scope for collections. Of course, the optimal solution would be to not have to write TWO active methods, but use one for both purposes.
(question already answered in the comments, but might as well write a proper answer)
It is not possible to use a query scope in a Colletion, since query scope is a concept used in Eloquent to add constraints to a database query while Collections are just a collection of things (data, objects, etc).
In your case, what you need to do is to change this line:
$association->members->active()->count();
to:
$association->members()->active()->count();
This works because when we call members as a method, we are getting a QueryBuilder instance, and with that we can start chaining scopes to the query before calling the count method.
I have a code in one of my controller files in laravel, below code is in Teamcontroller.php file:
$faqs = Faq::portal()->get()->toJson();
$glossaries = Glossary::portal()->get()->toJson();
Similarly, we have faq and glossary controller.
I am not able to understand what exactly the above code means. I tried to find function inside the Faq model and Faq controller and glossary model and controller, but i am not able to find any function portal.
Can anyone please explain to me what the above code means and where can I get reference to portal?
Laravel does a lot of magic, in your case it seems to be a Query Scope, from laravel documentation:
Scopes allow you to easily re-use query logic in your models. To define a scope, simply prefix a model method with scope
This is their example:
class User extends Eloquent {
public function scopePopular($query)
{
return $query->where('votes', '>', 100);
}
public function scopeWomen($query)
{
return $query->whereGender('W');
}
}
and it is called as follows:
$users = User::popular()->women()->orderBy('created_at')->get();
So in your case, you should have a method called portalScope in either the given class, or a parent one.
You can read about query scopes here
I have a pair of objects in laravel, pages and contents.
I have setup a relationship function, and include, which includes contents based on page_id.
I want to apply other conditions such as where deleted - 0, and where dates are within certain bounds, I know how to apply where conditions and set these field up.
What I can't fathom is how extra conditions can be applied as well as matching relationship fields.
Could anybody help me with this?
Defining the relationship in the model is all you need to do to the model itself. However you can add a static function to the model to get the data with the information you need.
Page model methods examples:
class Page extends Eloquent {
public function contents()
{
return $this->has_many('Content');
}
// Return all content that has not been marked as
// deleted in the database
public static function with_contents($id)
{
return Page::find($id)->contents()
->where_null('deleted')
->get();
}
}
This example assumes that the deleted field in the contents table is null unless it is deleted, in which case it will have any value.
To use you would simply use
$page = Page::with_contents('1');
You can create as many of these static helper methods as you like or add as many conditions to the one method as you like, then use your new static methods to retrieve your data.
I think this might be what you're looking for
http://doginthehat.com.au/2012/06/adding-conditions-to-relationships-in-laravel/
class User extends Eloquent {
public function meta()
{
return $this->has_many('Meta','target_id')
->where('target_type','=',$this->table());
}
}