laravel hide result for all relations if column has specific value - laravel

Lets assume I have a table posts with the fields id and content and published.
A User can have multiple posts, and a post can belong to multiple pages and there might be a lot more relations to a post.
Lets say we have an admin that wants to moderate the posts, the posts should only be visible if approved. So I add the boolean published where posts that are not published 0 should never be visible (only in specific cases e.g. to moderate the post).
Is it possible to set something in the Post model to restrict the related models from loading non published posts.
I want to avoid that I have to filter in the relation, e.g. if I call $user->posts I do not want to check if the posts are published, the non published results should not be available only if i do a search like. Post::where('published','0'). Basically something like softdeletes but than with a custom field.
An example, where the opposite relations are also defined, to make it easier to understand would be:
class Post extends Model
{
use SoftDeletes;
protected $table = 'posts';
public function collection()
{
return $this->belongsTo('App\Collection');
}
public function style()
{
return $this->belongsTo('App\Style');
}
public function pictures()
{
return $this->hasMany('App\Picture')->orderBy('priority', 'asc');
}
public function user()
{
return $this->belongsTo('App\User');
}
}

You can use global query scope in the Model as below to add your desired filters to each query, like below:
// Post.php
class Post extend Model
{
protected static function booted()
{
static::addGlobalScope('published', function (Builder $builder) {
$builder->where('published', true);
});
}
// ...
}
Whenever you don't want to apply the global query scope, use withoutGlobalScope with the name of the query scope, like below:
Post::withoutGlobalScope('published')->get();

Related

How to disable loading of relationships when not needed in Laravel

is it possible to disable the loading of relationships, but only in some cases?
Here are my models:
class League extends Model
{
...
public function country()
{
return $this->belongsTo(Country::class)->with('translations');
}
}
class Country extends Model
{
...
public function translations()
{
return $this->hasMany(CountryTranslation::class, 'country_id');
}
}
class CountryTranslation extends Model
{
...
}
In many places, I need to load the translations relationship for Country, but on some pages, I want to display information about the League and its Country only. There I don't want to show the CountryTranslation collection.
Here is the code for that page:
$country = $league->country;
Is it possible only for this line to disable the relations?
So, you're currently finding out one of the reasons for not defining the eager loading inside of the relationship. The first suggestion would be to remove the with() from the relationship definition, and add it in where needed. If desired, you can create another relationship that has the eager loading enabled, and it can use the base relationship to keep it DRY:
public function country()
{
return $this->belongsTo(Country::class);
}
public function countryWithTranslations()
{
return $this->country()->with('translations');
}
If this code change is not feasible, you will need to change how you're accessing the country relationship. When you access the relationship attribute, it lazy loads the relationship, and you don't have the ability to modify the relationship query. So, instead of accessing the relationship attribute, you'd need to call the relationship query so you can modify it.
Therefore, you won't be able to do $country = $league->country;, but you can do:
$country = $league->country()->without('translations')->first();
he with() simply eager loads the translations to avoid additional queries, but you should be able to load the translations with and without it, without with( adds additional queries. https://laravel.com/docs/9.x/eloquent-relationships#eager-loading
You will want to change:
public function country()
{
return $this->belongsTo(Country::class)->with('translations');
}
to
public function country()
{
return $this->belongsTo(Country::class);
}
If you want to load translations, you can do it in the controllers
// if you want translations at some point do this:
$league = League::with('country.translations')
$country = $league->country->translations
// if you do not want translations
$league = League::with('country')
$country = $league->country;
If you do not want to touch:
public function country()
{
return $this->belongsTo(Country::class)->with('translations');
}
you can create another method
public function countryClean()
{
return $this->belongsTo(Country::class);
}
$country = $league->countryClean;

In which Controller should I write logic for Models that are in a relationship

I am making a blog with users who each have posts. Naturally, I have a UserController, PostController, and a User and Post model.
The User model has a one-to-many relationship with the Post model like so
// User Model
public function posts()
{
return $this->hasMany('App\Post');
}
I would like to get all the posts of a specific user by using
// UserController or PostController...
$posts = User::find($id)->posts()->get()
Should I write this logic in my UserController (because it uses the User model) or should I write the logic in my PostController (because it returns posts)?
Actually, you just need to create the relationship as you did, and then you will be able call it wherever you want, just like as you described, with User::find($id)->posts()->get().
So, if you need to return a user's post on a view, for instance, you can do something like that on your UserController:
public function postsView(User $user)
{
$posts = $user->posts()->get();
return view('user.posts', compact('posts'));
}
Also, if you want the other way around (to know who is the user behind the post), you can create a relationship on your Post model using belongsTo().
// Post Model
public function user()
{
return $this->belongsTo('App\User');
}
You can write the query anywhere in UserController or PostController
$user = User::with('posts')->whereId($id)->first();
echo '<pre>';
print_r($user->toArray());
exit;
You'll get user with all posts
In your Model User put the relationship.
public function posts()
{
return $this->hasMany('App\Post','foreign_key','local_key');
}
In your UserController or PostController get the results by using
$posts = User::find($id);
foreach($posts->posts as $post)
{
echo $post->title;
}
You can query from both controllers. From where you want to query the relationship depends on where and how you want to fetch data from.
For example, if you are showing posts related to user on a user profile page with other bio data etc then you will call the relationship in UserController.
On other hand, let's say you want to show posts related to an author on a search page then it will be better to have the posts queried via relationship in PostController.
In short, it's a choice based on convenience and optimization to be made.

Query using two different relation laravel

I have relations like : a secteur has many sections, a section has many users, so a user belongs to a section and a section belongs to a secteur.
In Secteur model
public function sections()
{
return $this->hasMany(Section::class,'secteur_id');
}
In Section model
public function users()
{
return $this->hasMany(User::class,'section_id');
}
public function secteur()
{
return $this->belongsTo(Secteur::class,'secteur_id');
}
In User model
public function section()
{
return $this->belongsTo(Section::class,'section_id');
}
Now i'm trying to get all the users who belongs to a secteur, for that i need to retrieve all sections from the secteur and all users who belongs to all sections i've got.
I don't know how to do that.
ps:Sorry for my english i'm french
Eloquent has a hasManyThrough relationship which may be what you want.
On secteur, you can add this:
public function users()
{
return $this->hasManyThrough(User::class, Section::class);
}
As the docs state:
"The first argument passed to the hasManyThrough method is the name of
the final model we wish to access, while the second argument is the
name of the intermediate model."
You can also pass keys as additional arguments if you need to, but the ones you're using above look like the defaults and if they are you can omit them.

Laravel 4 - Display username based on ID

I have a posts and a users table.
In the posts table, i have a posts_author which is the ID of the user from the users table.
Is there an easy way to display the email address of the user, which is in the users table?
Cheers,
As long as you've set your relationships up it should just be a simple query.
http://laravel.com/docs/eloquent#relationships
Look at the one to many relationships.
(1 User, Multiple posts)
Remember to set the inverse of the relationship up also
If your model has the right relationships then should be as simple as $post->author->email().
You must tweak the author relationship because Eloquent assumes the key will be named author_id.
// Post
public function author() {
return $this->belongsTo('Author', 'posts_author');
}
// Author
public function posts() {
return $this->hasMany('Post');
}
Remember to use eager loading in case you are retrieving emails from more than one post object, or you will end up with n+1 queries.
Providing that you've configured the relationships properly, it should be pretty easy.
Post Model:
class Post extends Eloquent
{
protected $table = 'posts';
public function author()
{
return $this->belongsTo('User', 'posts_author');
}
}
Then User Model:
class User extends Eloquent
{
protected $table = 'users';
public function posts()
{
return $this->hasMany('Post', 'posts_author');
}
}
Then when loading the post you can do the following.
$post = Post::with('author')->find($id);
This will tell Eloquent to join on the users table and load the user data at the same time. Now you can just access all of the user information like this:
$post->author->username;
$post->author->email;
$post->author->id;
// etc etc
Obviously this is just a skeleton, but the assumption is that you have the rest setup.

Laravel 4.1: proper way to retrieve all morphedBy relations?

Just migrated to 4.1 to take advantage of this powerful feature.
everything seems to work correctly when retrieving individual 'morphedByXxxx' relations, however when trying to retrieve all models that a particular tag belongs to -- i get an error or no results.
$tag = Tag::find(45); //Tag model name = 'awesome'
//returns an Illuminate\Database\Eloquent\Collection of zero length
$tag->taggable;
//returns Illuminate\Database\Eloquent\Relations\MorphToMany Builder class
$tag->taggable();
//returns a populated Collection of Video models
$tag->videos()->get();
//returns a populated Collection of Post models
$tag->posts()->get();
My Tag Model class loooks like this:
class Tag extends Eloquent
{
protected $table = 'tags';
public $timestamps = true;
public function taggable()
{
//none of these seem to function as expected,
//both return an instance of MorphToMany
//return $this->morphedByMany('Tag', 'taggable');
return $this->morphToMany('Tag', 'taggable');
//this throws an error about missing argument 1
//return $this->morphToMany();
}
public function posts()
{
return $this->morphedByMany('Post', 'taggable');
}
public function videos()
{
return $this->morphedByMany('Video', 'taggable');
}
}
And the Post and Video models look like this:
class Post extends Eloquent
{
protected $table = 'posts';
public $timestamps = true;
public function tags()
{
return $this->morphToMany('Tag', 'taggable');
}
}
I am able to add/remove Tags to Posts and Videos as well as retrieve the related Posts, and Videos for any Tag -- however -- what is the proper way to retrieve all Models having the Tag name 'awesome'?
Was able to figure it out, would love to hear comments on this implementation.
in Tag.php
public function taggable()
{
return $this->morphToMany('Tag', 'taggable', 'taggables', 'tag_id')->orWhereRaw('taggables.taggable_type IS NOT NULL');
}
in calling code:
$allItemsHavingThisTag = $tag->taggable()
->with('videos')
->with('posts')
->get();
I just used this on Laravel 5.2 (not sure if it is a good strategy though):
Tag model:
public function related()
{
return $this->hasMany(Taggable::class, 'tag_id');
}
Taggable model:
public function model()
{
return $this->belongsTo( $this->taggable_type, 'taggable_id');
}
To retrieve all the inverse relations (all the entities attached to the requested tag):
#foreach ($tag->related as $related)
{{ $related->model }}
#endforeach
... sadly this technique doesn't offer eager load functionalities and feels like a hack. At least it makes it simple to check the related model class and show the desired model attributes without much fear to look for the right attributes on the right model.
I posted a similar question in this other thread as I am looking for relations not known in advance.

Resources