There are three tables in my system:
Students
Articles
categories
A student can write many articles and an article belongs to just one student. And an article can have only one category.
Article Model
class Articles extends Model
{
protected $fillable = ['id','title', 'body', 'students_id', 'created_at', 'updated_at'];
protected $table = 'articles';
public function students(){
return $this->belongsTo('App\Students');
}
public function categories(){
return $this->belongsTo('App\Categories');
}
}
I have created the above code, because I needed to get an articles list with who written by that article with the category name.
For that I used $article_list = Articles::get(); in the controller, and it works perfectly.
Then again I needed to get article list (this time I don't need the student name and category names; the output of the article table is more than enough).
But if I use $article_list = Articles::get(); it outputs the article table joining with the category and students table also.
Is there a way to get just the article table using Eloquent?
Relations within Eloquent are eager loaded so you are safe and it's no harm that categories are also being loaded. Quoted from the docs:
When accessing Eloquent relationships as properties, the relationship
data is "lazy loaded". This means the relationship data is not
actually loaded until you first access the property.
https://laravel.com/docs/5.4/eloquent-relationships#eager-loading
try :
class Articles extends Model
{
protected $fillable = ['id','title', 'body', 'students_id', 'created_at', 'updated_at'];
protected $table = 'articles';
public function students(){
return $this->belongsTo('App\Students');
}
public function categories(){
return $this->hasOne('App\Categories');
}
}
class Student extends Model
{
public function articles(){
return $this->hasMany('App\Articles');
}
}
you can try Has Many Through relationship type
official link: read more
#jjj's answer is the right one, but to explain in a bit more detail:
$articles = Articles::get();
will load the only articles. You can check it like this in your controller:
public function articles() {
$articles = Articles::get();
return $articles;
}
But $articles is a collection of models, and each model is "aware" of it's relationships. So if you try to access one of those relationships, Laravel will silently load it for you. So if you pass the same $articles above to your view (currently without categories), and then in your view do something like:
#foreach ($articles as $article)
{{ $article->categories->name }}
#endforeach
it will work, because Laravel is doing the SQL to find each article's category and then name. As #jjj explains, this is called Lazy loading and is described in the docs.
Incidentally lazy loading like this is usually inefficient, and it would be better to eager load, like you show in one of your comments above. It is described well in the docs.
Related
I have two Eloquent models:
1) Post
class Post extends Model
{
protected $table = 'posts';
protected $fillable = ['id', 'user_id', 'product_id', 'site_id', 'link_id', 'body', 'created_at', 'updated_at'];
public function user(){
return $this->belongsTo(User::class);
}
public function product(){
return $this->belongsTo(Product::class);
}
2) Product
protected $table = 'products';
protected $fillable = ['id', 'user_id', 'manufacturer_id', 'shift_product_id', 'name', 'english_name',
'slug', 'text', 'spec', 'live', 'created_at', 'updated_at'];
public function posts(){
return $this->hasMany(Post::class);
}
I need to get the product from a post
I do that:
$posts = Post::get();
foreach($posts as $key){
dd($key->product);
}
Like this it returns NULL
If I do like this:
dd($key->product());
I get the product but I can't to use that
but I need to get something like that to use whant I need:
Try to point out foregin key and other key in relation, examples:
public function post()
{
return $this->belongsTo('App\Post', 'foreign_key', 'other_key');
}
public function user()
{
return $this->belongsTo('App\User', 'foreign_key', 'other_key');
}
More: https://laravel.com/docs/5.5/eloquent-relationships
i found my problem
i dont have in the DB product with ID = 1
:/
stuped problem
thanks for all the help i leran alot from u.
The relationship probably doesn't exist in the database.
Based on your fillable array on Post, the way you have the relationships setup looks correct as you are following naming conventions for keys and your belongsTo relationship methods have the correct name for convention.
$post->product() is not returning your Product model. It is returning a Relation type object (BelongsTo). This is used for querying the relationship. $post->product would be the dynamic property for the relationship that would return the already loaded relationship or load the relationship and give you the result.
Laravel 5.5 Docs - Eloquent - Relationships - Relationship Methods Vs. Dynamic Properties
If the relationships are setup correctly $post->product being null would mean the relationship doesn't actually exist in the database, no matching id in products for product_id or product_id being null. (assuming no foreign key constraint)
Side note: eager loading the relationship would be a good idea:
$posts = Post::with('product')->get();
I just came across this post because I got a similar error while working on a project.
What I discovered is that when you query a model with the all() method, it ignores the related softdeleted rows.
When you try to access them tho, you get the null
Remember to hit save() after associate and dissociate. Got me a couple of times:
$model->relation()->associate($record)->save();
I am currently learning Laravel and I have an issue which I can't seem to find a solution for. I have a many-to-many relation between two tables that always returns nothing. Let me show you the basic setup:
My posts Model:
// App\Post.php
protected $fillable = [
'name',
'description'
'videopath'
];
public function Tags()
{
return $this->belongsToMany('App\Tag');
}
public function Cats()
{
return $this->belongsToMany('App\Cat');
}
My tags model:
// App\Tag.php
protected $fillable = [
'name',
'description'
];
public function exercises()
{
return $this->belongsToMany('App\Exercise');
}
My posts controller:
// PostController.php
public function show($id)
{
$post = Post::find($id);
return view('posts', ['post'=>$post];
}
The view:
// posts.blade.php
#foreach($post->tags() as $tag)
//do stuff
#endforeach
The intermediate table is called post_tag and contains the post_id and tag_id columns. At first it returned the results as expected but after some while all of my posts didn't return any tags anymore. The cats model looks similar to the tags model. Anyone has an idea?
Check the name of your Tags function. In your view you are calling "tags" instead of "Tags".
Have you created the intermediate table in your database? If so, check the naming convention (alphabetic order) that Laravel uses to find it: in your case it should be tag_post. if not, customize the name of the table when defining the relationship.
Many To Many
Many-to-many relations are slightly more complicated than hasOne and hasMany relationships. An example of such a
relationship is a user with many roles, where the roles are also
shared by other users. For example, many users may have the role of
"Admin". To define this relationship, three database tables are
needed: users, roles, and role_user. The role_user table is derived from the alphabetical order of the related model names, and contains the user_id and role_id columns.
Taking your view:
#foreach($post->tags() as $tag)
//do stuff
#endforeach
$post->tags() will return the relationship instead of the actual collection. You want $post->tags instead or $post->tags()->get().
i have three models
Article
id
title
Comment
id
title
user_id
article_id
User
id
name
what i wanna achieve is to select one article based on its id with comments and user info that made that comment
like that :
$article = Article::find($id -- say 1)->with('comments' -- this is a relation in Article Model)->get();
this gives me article with related comments as an array of objects say comment one - comment two etc ....
what i want instead of user_id in comment object i wanna it to be a user object
see this pic thats what i reached so far
using laravel 5.4
You can use following:
$articles = Article::find($id)->with('comments', 'comments.user')->get();
Here 'user' is the relationship you mentioned in the comments model for User.
If you have defined the foreign key relationship in Schemas, you can define functions for Eloquent Relationship as defined in following reference link -
Laravel - Eloquent Relationships.
You can define functions in models as follows -
Article -
class Article extends Model
{
...
public function comments(){
// Accessing comments posted to that article.
return $this->hasMany(\App\Comment::class);
}
// Create a foreign key to refer the user who created the article. I've referred it here as 'created_by'. That would keep relationship circle complete. You may ignore it if you want.
public define user(){
// Accessing user who posted the article
return $this->hasOne(\App\User::class, 'id', 'created_by');
}
}
Comment -
class Comment extends Model
{
...
public function article(){
// Accessing article to which the particular comment was posted
return $this->hasOne(\App\Article::class, 'id', 'article_id');
}
public function user(){
// Accessing user who posted the comment
return $this->hasOne(\App\User::class, 'id', 'user_id');
}
}
User -
class User extends Models
{
...
public function articles(){
// Accessing articles posted by a user
return $this->hasMany(\App\Article::class);
}
public function comments(){
// Accessing comments posted by a user
return $this->hasMany(\App\Comment::class);
}
}
Now you can use like following -
$article = Article::findOrFail($id);
$comments = $article->comments;
$article_user = $article->user;
$comment_user = Comment::findOrFail($commnet_id)->user;
$users_comments = User::findOrFail($user_id)->comments;
$users_articles = User::findOrFail($user_id)->articles;
and so on...
It is far better to use ->find() at last instead of ->get() because get() returns a Collection.
This way you will get a single object which you want instead of a Collection.
For example:
$commentableObj = Post::with(['comments'])
->withCount(['comments'])
->findOrFail($commentable->id);
I've got Tag and Attendee Eloquent models, they are in many-to-many relation. Pivot table has also two more attributes – value_int and value_string. My Attendee model looks like this:
class Attendee extends Model
{
public $timestamps = false;
protected $fillable = [
'event_id'
];
public function tags() {
return $this->belongsToMany('App\Models\Tag', 'attendee_tag', 'attendee_id', 'tag_id')
->withPivot(['value_string', 'value_int']);
}
public function scoreTagValue($tag_id) {
return $this->tags->where('tag_id', '=', $tag_id)->first();
}
}
What I want is to obtain pivot values based on Attendee model and variable tag_id, so I've written scoreTagValue function, but it always returns null and I don't know why :( I'm calling it this way:
$attendee->scoreTagValue($tag_id). Thanks for your help :)
You need to access the relation, not the property:
public function scoreTagValue($tag_id) {
return $this->tags()->where('tag_id', '=', $tag_id)->first();
}
Also, according to the docs, withPivot() does not take an array, so:
->withPivot('value_string', 'value_int');
I'm using Laravel 5.4.22 (the newest one). In MySQL, I have two tables, tag_categories and tags, which form a many-to-many relationship. What I need is a query which returns all the tags for the selected categories. I know how to solve this when I have only one object, and I know how to solve this with querying and looping each of those objects, but there has to be a query or eloquent based solution for the whole thing?
I understand the code below doesn't work because I'm using ->belongsToMany on a collection rather than an object, but how to I bridge this gap the simplest way?
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)
->belongsToMany(Tag::Class)->get();
dd($resultingTags);
belongsToMany generally belongs in the model class, not a method called on the fly. When looking to eager load the relationship, you then call the with() method on the query builder.
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many
ex:
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany('App\Role');
}
}
// Query
$users = User::with('roles')->get();
$rolesOfFirstUser = $users->first()->roles;
If you're trying to get all the tags of the given categories, then you should be querying tags, not tag_categories.
Tag::whereHas('categories', function ($query) use ($chosenCategoriesIds) {
$query->whereIn('id', $chosenCategoriesIds);
})->get();
This is One-to-many relation
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->hasMany('App\Tag');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();
If you want define Many-To-Many relation for this case
You need to have 3 tables tags, tag_categories, tag_tag_category
Define relation at TagCategory model at app/TagCategory.php
public function tags()
{
return $this->belongsToMany('App\Tag', 'tag_tag_category', 'tagcategory_id', 'tag_id');
}
And handle at your Controller
$resultingTags = TagCategory::whereIn('id', $chosenCategoriesIds)->with(['tags'])->get();