I am learning Laravel 5.2 and can see the huge potential of Eloquent, but I find some of the tutorials rather assuming that one understands things. For example I have a table authors and a table items. Obviously authors has many items and items have 1 author.
The various examples and videos tend to jump over this and what I would like to understand is the rules and a good source for setting up the relationships and more complex queries otherwise I will have to resort to using PDO classes of a more trandition manner.
Can someone recommend something please?
Personally I think the Laravel documentation has improved a lot and the topic of Eloquent is quite clear. For instance, your example is clearly a one to many relationship.
class Author extends Model
{
public function items()
{
return $this->hasMany('App\Item');
}
}
class Item extends Model
{
public function author()
{
return $this->belongsTo('App\Author');
}
}
As simple as that and so elegant. Would you mind rephrase what is it that's confusing to you if I am mistaken?
Related
Context:
So let's say I have 3 tables: Genres, Books, Authors
and Each Book has a Genre and Author(s).
What I want to happen is return a list of Genres with all the related Books with Authors. Basically, group Books by Genre and include book authors. This is what I'm doing.
Genre->with(['books' => function($q) { $query->with('authors'); }]);
Problem I'm having is the speed since I have a lot of books and double that number is the number of authors. The speed is okay when Authors is not included and adds around 4sec when included which is not good since I just need the name of the Author - sometimes just one Author when the book has multiple author.
I'm thinking of creating an accessor in Books model to get just get the name of one Author related to it. I'm still figuring out how to do it and how I can append that accessor in controller because I don't want to use the protected $appends since it is not always needed.
Do you think it is better to use accessor or is there a better way to do what I wanted to happen?
You could adjust authors relationship to always return just one author (modify orderBy clause as needed).
$genre = Genre->with(['books.authors' => function($query) {
$query->orderBy('name')->limit(1);
}])->first();
And when fetching the author, you can assume that first entry is the one you need.
$genre->books->each(function (Book $book) {
$book->title;
$book->authors->first()->name;
});
To increase performance, make sure that your foreign keys ar indexed.
everyone:
I'm trying to create an application with several many to many relations, including a m-m rel onto the same model. It's a headache. In the official docs there is no so much information. I've been looking the code for belongsToMany method, in HasRelationShips.php, but there are not description on the parameters use.
Where can I get detailed documentation about the use of the parameters, in order to learn the right way to create any kind of relationships?
Do you know any book or document which details the working of all of the methods and parameters, so I can read it and REALLY learn how do they work?
Thanks everyone
Bro, in the DOCs you can find it easily.
Belongs to Many is like that:
public function companys(){
return $this->belongsToMany('App\Users', 'companys_users', 'user_id', 'company_id')->withTimestamps();
}
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many
We have the models: Question and Answer.
In the answers table, we have a boolean column with the name accepted and we have just one accepted Answer. We want to get question with its answers and accept answer separately.
I use two approaches, but I don't know which is better:
1). check if the relation is loaded if loaded filter relations and get an accepted answer like this:
if(!$this->relationLoaded('answers')){
$this->load('answers');
}
return $this->answers->firstWhere('accepted',true);
2). We can use this approach too:
function accepted_answer(){
return $this->answer()->where('accepted',true);
}
The problem of the second approach is we get an array, but we expect a single response or null.
Is there any better approach to this or we can use neither 1 or 2?
Create an Accessor on Question model.
class Question extends Model
{
public function getSelectedAnswerAttribute()
{
return $this->answers->where('accepted', true)->first();
}
}
now in every Answer instance has an attribute called selected_answer.
You can access it like,
$answer->selected_answer;
Note
this 'selected_answer' is not fetching from database, it filters from already fetched answers list. so its more efficient than your 2nd approach.
and, If you want this attribute automatically bind to the Question model always. add this accessor to appends array.
Model
protected $appends = ['selected_answer'];
create accepted scope at your answer model.
public function scopeAccepted($query){
return $query->where("accepted",true)->first();
}
then you can easly retreive first accepted answer.
$question->answer()->accepted();
for more further check out query scopes;
You should go with the second approach as you will have a single source to change later on if something changes (instead of digging through a lot of controllers)
To get the result, use ->get(); at the end of builder query.
P.S: Scopes are generally always better than writing queries manually inside the controller
I have a guitar lessons site using Laravel 5.3. It has lessons that are then broken down into exercises.
I decided today to add in the backend the ability to mark lessons or exercises as published. I simply added a 'published' column in the respective tables which contains 0 or 1.
There are many places in my site where exercises or lessons are accessed in some shape or form, including a lesson view, exercise view, lessons partial view (a tabular listing), exercises partial view (also a tabular listing), search/query views. There are also routes that perform functions such as incrementing hit count for a lesson or exercise, adding a lesson or exercise to one's favorites.
Given above, I am not sure how to prevent access to unpublished lessons or exercises using minimal code edits. I started looking at the controller files but then realized the controller might not be a catch all, such as on query result pages. Also controller files contain multiple methods and I don't want to edit them separately. I suspect there is a "laravel way" to do what I need globally, without me having to modify a bunch of queries in multiple files. Maybe it amounts to middleware or something?
In any case, I am new to laravel and if there is a tried and true way to control routes for content that is marked as unpublished, I would be very interested to know about it.
thanks
Take a look at global scopes, laravelest way to do this
As an aside, I prefer to use timestamps for things like published states on models. It means I can set a future timestamp for that content to become available, i.e. if I’m going on holiday.
In terms of your actual problem, a query scope would be the way to go:
class Lesson extends Model
{
public function scopePublished($query)
{
return $query->wherePublished(true);
}
}
Alternatively, if you did use a timestamp instead, you could do:
class Lesson extends Model
{
public function scopePublished($query)
{
return $query->where('published', '<=', $this->freshTimestamp());
}
}
You will then need to update your front-end code to use this scope:
public function LessonController extends Controller
{
public function index()
{
$lessons = Lesson::published()->latest()->paginate();
return view('lesson.index', compact('lessons'));
}
}
Newbie here. I'm learning Laravel and WardrobeCMS is a good one to study IMO. Just wondering if you guys could point me how they retrieve posts with requesting db?
In homepage, the HomeController has this line.
$posts = $this->posts->active(Config::get('core::wardrobe.per_page'));
In WardrobeServiceProvider, they bind PostRepoitory with DbPostRepository and here is their 'active' method.
public function active($per_page)
{
$per_page = is_numeric($per_page) ? $per_page : 5;
\Debugbar::info('calling posts active method');
return Post::with(array('tags', 'user'))
->where('active', 1)
->where('publish_date', '<=', new DateTime)
->orderBy('publish_date', 'desc')
->paginate($per_page);
}
The return statement looks like the Eloquent command but I don't see any db request in Debugbar console. How does this work? Please advise.
I am a designer trying to learn coding and Laravel 'illuminates' me. In my current dev project, I have around 4-10 db calls in each page. My goal here is to reduce db calls as much as possible and I think WardrobeCMS source code is my starting point.
Post::with()->where()->paginate(); doesn't always 'call' database?
In short: Yes it does!
Post is an Eloquent ORM because, basically every model (i.e. Post) in Laravel extends the Eloquent/Model class and this Eloquent/Model class uses Query Builder (DB in your case) class along with other classes to query the database and in your example, the Post is something like this:
class Post extends Eloquent {
// ...
}
SInce the Post class extends the Eloquent class so Post have access (inherited from Eloquent) to all (protected and public only) methods of Eloquent class, so, if the Eloquent has a function/method like this (actually has):
public function with($relation)
{
//...
}
Then the Post class can call that function/method like this:
Post::with()
So, actually Post::with()->where()->paginate() call interact with database in this case but it's not necessary that every database interaction has to be using DB::table(), instead, it's a convenient way to query the database using an ORM and don't forget that, the ORM is just a wrapper over the query builder, behind the scene, the Post Eloquent Model is using the query builder.
You should the manual instead of reading the source code of another application, once you get the basic idea then you may read source code of other Laravel applications to get some ideas that other developers uses.
Update: If you are not seeing any queries in the debugger then the application is using the caching feature of Laravel to cache database queries for a certain period of time so it can use those later without interacting with the database to speed up the process.