Laravel 5 Polymorphic Relationship - laravel

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.

Related

Removing duplicates in Laravel relationships

I'm working with a situation where people and employers are related through licenses. So my models look something like this:
class Employer extends \Illuminate\Database\Eloquent\Model
{
public function people()
{
return $this->belongsToMany(Person::class, 'licenses');
}
// &c
}
class Person extends \Illuminate\Database\Eloquent\Model
{
public function employer()
{
return $this->belongsToMany(Employer::class, 'licenses');
}
// &c
}
However, person-employer pairs can have multiple licenses. So the aforementioned relationship returns duplicate Person entities, namely one Person entity for every License entity associated with the employer.
How do I fix this issue so that these relationships only return unique entities?
You can try laravel $collection->unique('licence_number')
so whatever eloquent returns you in collection just use unique with attribute
on the basis of you want to make the result unique like
licence_number is in the collection
[ { licence_number:1, name:'test' }, { licence_number:2, name:'jhon' } ]

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";
}

A model with polymolymorphic ony to many and simple one to many relationship

I am using laravel 5.7. I have now a situation where i have a Fight model with structure user_id, fightable_id
i have two other tables users and monsters. so users_id refers to users (a user can have many fights) and fightable_id can refer to either a user or a monster (monsters table). so I have to define the functions for the relation ship
for User model i have to do
1.for polymorphic one to many relationship
public function fights()
{
return $this->morphMany('App\Fight', 'fightable');
}
2.for simple one to many relationship
public function fights()
{
return $this->hasMany('App\Fight');
}
I am confused now. ofcourse the only way is to change the functions name. but i will be doing the correct thing by changing the function names right (as both the functions have same name). or is there anything I am doing wrong?
I'm not sure I understand your question completely but I'll try my best!
Also this post will really help you understand the problem you are facing I think,
Laravel morph relationship
Using the code from the post I linked but taking your tables would produce models defined like this.
User Model
class User extends Model
{
public function fights()
{
return $this->hasMany('App\Fight');
}
}
Fight Model
class Fight extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function fightable()
{
return $this->morphTo();
}
}
Monster Model
class Monster extends Model
{
public function fight()
{
return $this->morphOne('App\Fight', 'fightable');
}
}
If you still feel this has not answered your question or need some more help just let me know!

How to setup conditional relationship on Eloquent

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.

How can I add/integrate a general site comment to my existing commenting system in laravel?

I have got a commenting system something like below. User can comment articles, images and posts. Everything works fine. But additional to this I want to add a section where the user can comment the website itself, something like a general comment. I want to integrate this to the existing commenting system. But naturally there is no table or model for the site itself where I could add a relationship. What would be a good way to go?
tables are like:
articles
id - integer
title - string
body - text
posts
id - integer
title - string
body - text
images
id - integer
title - integer
path - text
comments
id - integer
comment - text
commentable - integer
commentable_type - string
and models are something like:
class Post extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Images extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Article extends Model
{
public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}
class Comment extends Model
{
public function commentable()
{
return $this->morphTo();
}
}
Since you only have one site you can set your morph fields to be nullable and add a scope to your Comment model that would return only those comments which are related to your site, like this:
public function scopeForWebsite($query) {
return $query->where(function($query) {
$query->whereNull('commentable')->whereNull('commentable_type');
});
}
and then retrieve your website comments using:
Comment::forWebsite()->get()
When creating comment just don't attach it to any entity and it will be treated as a global, generic one.
Just remember that you will need to manually add commentable and commentable_type fields in your migration (instead of assumed $table->morphs('commentable')), mark them as nullable and index those fields.

Resources