How to setup conditional relationship on Eloquent - laravel

I have this (simplified) table structure:
users
- id
- type (institutions or agents)
institutions_profile
- id
- user_id
- name
agents_profile
- id
- user_id
- name
And I need to create a profile relationship on the Users model, but the following doesn't work:
class User extends Model
{
public function profile()
{
if ($this->$type === 'agents')
return $this->hasOne('AgentProfile');
else
return $this->hasOne('InstitutionProfile');
}
}
How could I achieve something like that?

Lets take a different approach in solving your problem. First lets setup relationship for the various models respectively.
class User extends Model
{
public function agentProfile()
{
return $this->hasOne(AgentProfile::class);
}
public function institutionProfile()
{
return $this->hasOne(InstitutionProfile::class);
}
public function schoolProfile()
{
return $this->hasOne(SchoolProfile::class);
}
public function academyProfile()
{
return $this->hasOne(AcademyProfile::class);
}
// create scope to select the profile that you want
// you can even pass the type as a second argument to the
// scope if you want
public function scopeProfile($query)
{
return $query
->when($this->type === 'agents',function($q){
return $q->with('agentProfile');
})
->when($this->type === 'school',function($q){
return $q->with('schoolProfile');
})
->when($this->type === 'academy',function($q){
return $q->with('academyProfile');
},function($q){
return $q->with('institutionProfile');
});
}
}
Now you can access your profile like this
User::profile()->first();
This should give you the right profile. Hope it helps.

you can do this by use another method please check this:
a blog Post and Video model could share a polymorphic relation to a
Tag model. Using a many-to-many polymorphic relation allows you to
have a single list of unique tags that are shared across blog posts
and videos. First, let's examine the table structure:
https://laravel.com/docs/5.4/eloquent-relationships#many-to-many-polymorphic-relations

Looks like that should be $this->type rather than $this->$type - since type is a property, not a variable.

Related

How to add my course Slug before lecture in url path?

i'm currently doing a project for my homework, which i didn't managed to see something like this to fix my problem.
I have 2 models with Course and CourseLecture name. and i'm using getPathAttribute in my class base on a tutorial in internet.
in my Course Model i'm using getPathAttribute like this :
public function getPathAttribute()
{
return "/clientside/instructor/courses/$this->slug";
}
and in my CourseLecture Model :
public function getPathAttribute()
{
return "/clientside/instructor/courses/{course}/lectures/$this->slug";
}
I need to put my course slug to this getPathAttribute like :
http://url.com/clientside/instructor/courses/php/lectures/one
also my CourseLecture is using course_id and i've got a relationship between them which, they're belongsTo' andHasMany'. so how can i add course slug base on this structor in this path?
Also for this homework, i'm using Vue.js/Laravel. and it's a spa. i've tagged vue.js bcs if there's any solution to fix this via router, I will be happy to use it.
You can dynamically grab course slug from the course() relationship defined on the CourseLecture model. See below:
In CourseLecture model:
protected $appends = ['path'];
// relationship
public function course()
{
return $this->belongsTo(Course::class);
}
public function getPathAttribute()
{
return "/clientside/instructor/courses/{$this->course->slug}/lectures/$this->slug";
}

laravel call to undefined method addEagerConstraints()

I have two models
Post.php
id
post
show_id
type = 'movie' or 'tv'
Show.php
id // this only auto increment counter ids
show_id
show_type = 'movie' or 'tv'
the thing is show can be either tv or movie and may two with the same show_id for exmaple one tv could have a show_id of 10 and also one movie can have it but the types are diffrent
i have in post model
public function show(){
return $this->belongsTo('App\Show', 'show_id');
}
in show model
public function post(){
return $this->hasMany('App\Post', 'id');
}
this relationship get the first show with matching show id it sees, wheather its a movie or tv, i want to restrict it to match type column on both sides
post.php:
public function show() {
return $this->belongsTo('App\Show', 'show_id', 'show_id')
->where('type', $this->type);
}
show.php
public function posts() {
return $this->hasMany('App\Post', 'show_id', 'show_id')
->where('type', $this->show_type);
}
UPDATE (the code above does not work!)
Trying to use where clauses (like in the example below) won't work when eager loading the relationship because at the time the relationship is processed $this->f2 is null.
Read more here: Compoships
I just came accross a package https://github.com/topclaudy/compoships
what it does it allows creating relationships based on more than one FK, which laravel doesnt support by default
I think what you're looking for is a polymorphic relation. Instead of having a model that may be one of two "types" there should probably be two separate models on the same relation. For example:
class Post
{
public function Show()
{
return $this->morphTo();
}
}
class TvShow
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
class Movie
{
public function Post()
{
return $this->morphMany('App\Post', 'show');
}
}
Then your posts table would have a show_id and show_type field that would relate to either a tv show or movie. Check out the docs for more info, I'm not sure of the rest of your project so I'm not 100% this will fit but anytime you start putting "_type" fields in your table you should question whether or not you should be using a polymorphic relation. This will also likely keep your models cleaner and free of a bunch of if statements as you realize there are other differences between movies and shows and how you handle them.
https://laravel.com/docs/5.7/eloquent-relationships#polymorphic-relations

Access Method in a hasManyThorugh

I have 4 tables,
props, listing, offers, contact
props has many listing, listing belongs to props
public function listings()
{
return $this->hasMany('App\Models\Listing\Listing');
}
offer belongs to listing,
public function property()
{
return $this->belongsTo('App\Models\Property\Property')->with('owners');
}
then
offer belongsToMany contact trough offer_contact table
public function buyers()
{
return $this->belongsToMany(Contact::class, 'offer_contact', 'offer_id', 'contact_id')->with('primary_email');
}
My question is, how to access buyers()?
Something like $props->buyers()
In props model, what I did is
return $this->hasManyThrough('App\Models\Offer\Offer', 'App\Models\Listing\Listing');
You cannot. You may use nested iterations to get properties, listings belongs to each property, offers belongs to each listing and then customers belonging with the offer.
Alternatively, you may use the raw query to get the desired result using DB::statement();
I created a HasManyThrough relationship with unlimited levels: Repository on GitHub
After the installation, you can use it like this:
class Property extends Model {
use \Staudenmeir\EloquentHasManyDeep\HasRelationships;
public function buyers() {
return $this->hasManyDeep(Contact::class, [Listing::class, Offer::class, 'offer_contact']);
}
}

How to return two one to many relationships in an API response with Laravel controller?

So, I have three models - Events, Groups and Individuals. I'm trying to create an API response that provides a list of groups AND the individuals in each group. I've been able to create a response that provides a list of the groups, but I'm not sure how to also PROPERLY include the individuals of each group.
I thought about just looping through each from the first response and making another query for each, but that seemed like overkill. So, the question is how can I include the Individuals for each Group within the response as well.
I have all three eloquent models defined as such:
Events:
public function groups()
{
return $this->hasMany('App\Group');
}
Groups:
public function event()
{
return $this->belongsTo('App\Event');
}
public function individuals()
{
return $this->hasMany('App\Individual');
}
Individuals:
public function group()
{
return $this->belongsTo('App\Group');
}
Then I have my route:
Route::get('/events/{id}/groups', 'EventsController#groups');
Then that controller has the function to return the list of groups:
public function groups($eventId)
{
$event= Event::find($eventId);
$groups= $event->groups()->paginate();
return $groups;
}
Any help would be greatly appreciated. To clarify, I'd be looking for a response that looks something like:
{
id: 1,
group_name: 'Group Name',
individuals: [ .. array of individuals .. ]
}
Or if someone could give feedback on a better or more standard way to do it, I'd be open to that as well.
You need to use Eager Loading with the with() method:
public function groups($eventId)
{
$event= Event::find($eventId);
$groups= $event->groups()->with('individuals')->paginate();
return $groups;
}
Since you already know event ID, you can use Group model directly and load individuals by using with() method. This code will create just two queries and it's less verbose:
public function groups($eventId)
{
return Group::where('event_id', $eventId)->with('individuals')->paginate(5);
}

Laravel 5 Polymorphic Relationship

Not entirely clear on this yet as it would be the first time I'm tinkering with this. My table structure is as follows:
questions
- id
- type_id
multiple_choice_options
- id
- question_id
drag_and_drop_options
- id
- question_id
The type_id field on the questions table determines which options table to load from. So essentially I'd like to setup a relationship on the Question model as follows:
class Question extends Model {
public function options() {
// not sure what to return here?
}
}
And for the option models would this be the correct inverse definition?
class MultipleChoiceOption extends Model {
public function question() {
return $this->belongsTo(Question::class);
}
}
class DragAndDropOptions extends Model {
public function question() {
return $this->belongsTo(Question::class);
}
}
How do I set this up to work with polymorphic relationships?
You could simply build a switch in the options() relation:
class Question extends Model {
public function options() {
if ($type_id === 'multiple') {
return $this->hasMany(MultipleChoiceOption::class);
} else {
return $this->hasMany(DragAndDropOptions::class);
}
}
}
Careful though with these magic relations, you'll need additional type checks and careful coding in your app.
Check out the Laravel Docs on Polymorphism, there are some functions you could leverage with a slightly different data structure as such:
questions
- id
- optionable_id
- optionable_type
multiple_choice_options
- id
drag_and_drop_options
- id
Models:
class Question extends Model {
public function optionable() {
return $this->morphTo();
}
}
class MultipleChoiceOption extends Model {
public function question() {
return $this->morphMany('App\Question', 'optionable');
}
}
class DragAndDropOptions extends Model {
public function question() {
return $this->morphMany('App\Question', 'optionable');
}
}
Note, the optionable_type column will contain the class name of the owning model.
A side note, your data structure relationship logic seems a little strange to me but may still serve what you are trying to do. I would've probably had models Question, Option, QuestionType so that options are independent of question and question type. This would enable you to change the question type without having to change the options.
Considering what you are trying to achieve:
a)An option can only belong to a one question,
b)A question can have many options,
I think a cleaner solution might involve adjusting your database design,
questions
- id
- body
- question_type_id
question_type
- id
- name
options
- id
- name
- question_id
class question extends Model {
public function options() {
$this->hasMany('App\option','question_id')
}
}
class option extends Model {
public function question() {
return $this->belongsTo('App\question');
}
}
your question_type table would then contain muiltiple_choice,drag and drop, etc. therefore there would be no need for you to use an 'if' statement or 'switch', which makes your code cleaner. you also do not need to edit your code whenever you add a new question type.

Resources