Laravel One to One Polymorphic Relation - php

There are 3 models and their respective tables
1.Customer - id, name
2.Address - id, name
3.ExternalMapping - model_id, model_type, external_id
How do I define the relation in Customer model that will give me the external_id in ExternalMapping model?
I tried this but it didn't work
class Customer extends Model
{
public function cascades()
{
return $this->morphMany('App\ExternalMapping', 'external');
}
}
class ExternalMapping extends Model
{
public function external()
{
return $this->morphTo();
}
}

You need one to one polymorphic relation. Not one to many relation. So do this.
class Customer extends Model
{
public function cascade()
{
return $this->morphOne('App\ExternalMapping', 'external');
}
}
class ExternalMapping extends Model
{
public function external()
{
return $this->morphTo();
}
}
print_r($customer->cascade->external_id;)

The column you're referencing on your Customer class is incorrect, you should be referencing the model column not the external column as you currently are:
class Customer extends Model
{
public function cascades()
{
return $this->morphMany('App\ExternalMapping', 'model');
}
}
That will tell Laravel to look for a column on your ExternalMapping table that has the name model_id with the id value of the current Customer.
Then to grab the external_id:
$customer->cascades->external_id;

Related

Touching Eloquent model via polymorphic relationship

Laravel 5.7. I have two models, Foo and Content. Their relationship is polymorphic because Content can also be related to other models:
class Foo extends Model
{
public function contents()
{
return $this->morphToMany('App\Content');
}
}
class Content extends Model
{
use LastModified;
public function foos()
{
return $this->morphedByMany('App\Foo');
}
}
I want to touch the Foo model whenever the Content model is updated. So I use a LastModified trait for the Content model:
trait LastModified
{
protected static function bootLastModified()
{
static::updating(function ($model) {
static::updateLastModified($model);
});
}
protected static function updateLastModified($model)
{
$foos = $model->foos;
if (count($foos) > 0) {
foreach ($foos as $foo) {
$foo->touch();
}
}
}
}
My problem is that $model->foos returns the correct Foo models, but with the wrong ids. Instead of the Foo's own model id, each Foo has the id of the pivot table row. This means that the wrong Foo row is touched.
Laravel has built-in functionality for touching parent timestamps.
On the content model, you can add a property telling which relationships should be touched when the given model is updated.
The following should work:
class Content extends Model
{
protected $touches = ['foos'];
public function foos()
{
return $this->morphedByMany('App\Foo');
}
}
Edit: Since you are using a static updated event you should manually call $model->touchOwners() from the static::updated

Laravel DRY nested eager loading

Suppose I got a model with a few relations:
class Author extends Model
{
public function shortStories()
{
return $this->hasMany(ShortStory::class);
}
public function essays()
{
return $this->hasMany(Essay::class);
}
public function books()
{
return $this->hasMany(Book::class);
}
}
Now suppose I got two more models that want to eager load this model with its relations:
class Publisher extends Model
{
public function scopeWithAuthor($query)
{
$query->with('author.shortStories', 'author.essays', 'author.books');
}
}
class Reviewer extends Model
{
public function scopeWithAuthor($query)
{
$query->with('author.shortStories', 'author.essays', 'author.books');
}
}
Problem - if the relations of author change I now need to reflect this in multiple locations.
My question - how do I achieve this in DRY style?
I know that I could add a protected $with to the Author class but then it would always load the relation, not just when required.
With that in mind, one solution I have come up with is to extend the Author model like so:
class AuthorWithRelations extends Author
{
protected $with = ['shortStories', 'essays', 'books'];
}
This then allows me to refactor the scopes of the other models like so:
class Publisher extends Model
{
public function scopeWithAuthor($query)
{
$query->with('authorWithRelations');
}
}
class Reviewer extends Model
{
public function scopeWithAuthor($query)
{
$query->with('authorWithRelations');
}
}
This works well enough but really I was wondering if Laravel provides a better / inbuilt approach to this?

How to define the "follow" relationship in laravel 5.2

Recently I use Laravel to define the 'follow' relationship between users. Here are the models:
class User extends Model
{
public function follows()
{
return $this->morphMany('App\User', 'followable');
}
}
class Follow extends Model
{
public function followable()
{
return $this->morphTo();
}
}
and the database for Follow is like this:
follows:
- id:
- user_id:
- followable_id:
- followable_type:
all the above are defined according to the examples which Laravel documents provides.
And now I can retrieve the user model like this:
$follow = Follow:find(1);
$user = $follow->followable;
But when I write code like this:
$followers = $user->follows;
I get errors:
Relationship method must return an object of type
Illuminate\Database\Eloquent\Relations\Relation
here is my question:
Did I define the relationship of 'follow' right? and how can I fix the errors?
Thanks.
try this
class User extends Model
{
public function follows()
{
return $this->morphMany('App\User', 'followable');
}
}
change in to
class User extends Model
{
public function follows()
{
return $this->morphMany('App\Follow', 'followable');
}
}
because your have given model name is incorrect.

Laravel 5 database relationship accessing a data on another table

I have this kind of database design
user_classes
- id
- user_id
- class_schedule_id
class_schedules
- id
- class_id
- date
classes
- id
- name
I am now in my UserClass.php Model File
public function classSchedule() {
return $this->belongsTo('\App\ClassSchedule');
}
public static function getClassByUser($user_id){
$user_class = self::where('user_id','=',$user_id)->with('classSchedule');
//other codes here...
}
My question here is that how can I access the name of the class in the class table since the user_classes table doesn't have a direct access to the class instead it should go through first to the class_schedules table.
I am not sure what Eloquent ORM Relationship should I use.
Your help will be greatly appreciated!
thanks! :)
First of all you will need to rename the third class, from class to something else. Then try this,
class user_classes Extends Eloquent {
function classSchedule() {
return $this->hasMany('Class_schedules','class_schedule_id');
}
}
class class_schedules Extends Eloquent {
function userClasses() {
return $this->belongsTo('user_classes', 'class_schedule_id');
}
function classSomething() {
return $this->hasOne('class_something','id');
}
}
class class_something Extends Eloquent {
function classSchedules() {
return $this->belongsTo('class_schedules', 'id');
}
}
Try and follow the naming conventions within Laravel, that will make your life easier down the road.
It uses the snake_cased version of the plural of your model name to define the table name automagically.
Besides that, when you have a relation with single or plural output, name your relation methods accordingly to describe what they do and what kind of output you can expect.
I prefer a dir /app/Models for the models, hence the namespace, you can change this to /app if that's where your models are.
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UserClass extends Model
{
// only defined because laravel auto generates alphabetically
protected $table = 'user_classes';
public function user()
{
// given that User model was moved to app/Models, if not, use \App\User
return $this->belongsTo('\App\Models\User');
}
public function classSchedule()
{
return $this->belongsTo('\App\Models\ClassSchedule');
}
}
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ClassSchedule extends Model
{
public function class()
{
return $this->belongsTo('\App\Models\Class');
}
}
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Class extends Model
{
public function classSchedules()
{
return $this->hasMany('\App\Models\ClassSchedule');
}
}
Now you can basically fetch all entries of UserClass for a particular user, with or without eager loading...
$userClasses = UserClass::where('user_id', $userId)->get();
$userClasses->map(function($userClass) {
echo $userClass->classSchedule->class;
});
More preferably you'll have a method userClasses() with a hasMany relation in your user model

Laravel Relationships Between Two Models

I am struggling with working with relationships right now and would like some help as for how to make this relationship work. I am using Laravel.
Lets say you have a staff model that looks like so:
Staff.php
class Staff extends \Eloquent {
protected $fillable = [];
public function status()
{
return $this->belongsTo('Staff_status');
}
}
The database table for the staff is as follows:
Table Name: staff
Fields: id, staffer_name, status_id
You also have a staff status model represented below:
Staff_statuses.php
class Staff_status extends Eloquent {
public function staff()
{
return $this->hasMany('Staff');
}
}
You also have a staff database table like so:
Table Name: staff_statuses
Fields: id, status_name
However when I try and load the staff controller index method it says class Staff_status is not found.
Any idea why?
You have used Staff_statuses.php as the name of your model but you are using Staff_status in the class name and thus you are calling it using Staff_status from your controller as well. This is the problem.
Change the file name to match the class name. For example, use something like this:
// StaffStatus.php
class StaffStatus extends Eloquent{
protected $table = 'staff_statuses';
public function staff()
{
return $this->hasMany('Staff');
}
}
// Staff.php
class Staff extends Eloquent {
protected $table = 'staff';
protected $fillable = [];
public function status()
{
return $this->belongsTo('StaffStatus');
}
}
In the controller you may use something like this:
$staffStatus = StaffStatus::all();
$staff = Staff::all();
namespace App;
use Illuminate\Database\Eloquent\Model;
class Photo extends Model
{
public function imageable()
{
return $this->morphTo();
}
}
class Staff extends Model
{
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}
class Product extends Model
{
public function photos()
{
return $this->morphMany('App\Photo', 'imageable');
}
}

Categories