Eloquent model inheritance - multi-table design - php

Assume we have two db tables: posts and threads:
threads
id - integer
title - string
posts
id - integer
body - text
created_at - timestamp
thread_id - integer (fk)
and two Eloquent models:
class Post extends Model {
public function thread()
{
return $this->hasOne('App\Thread');
}
}
class Thread extends Post {
protected $table = 'threads';
public function post()
{
return $this->belongsTo('App\Post');
}
}
What I want to achieve is Thread object having id, title, body, created_at attributes, while Post object having id, body, created_at attributes.
Yet, I still get error:
Column not found: 1054 Unknown column 'body' in 'field list'
which is MySQL error that basically means that Laravel's trying to look up the body column in the threads table. However, it is stored in the posts table.

What you are trying to do is unnecessary with Eloquent.
The data that is retreived from the database iss storred in the $attributes property.
So unless you are sharing any behaviour you don't need to inherit from anything other than Model.

OK, so here is my updated answer after some research on your question. As you have defined table schema i.e
threads
id - integer
title - string
posts
id - integer
body - text
created_at - timestamp
thread_id - integer (fk)
This means Thread has many or just one (depends on relation) Post, and Post belongs to Thread, so the relationship would be something like this
class Thread extends Model{
protected $table = 'threads';
public function post()
{
return $this->hasMany('App\Post');//Or hasOne('App\Post')
}
}
class Post extends Model {
public function thread()
{
return $this->belongsTo('App\Thread');
}
}
What you want to achieve is Thread object having id, title, body, created_at attributes, while Post object having id, body, created_at attributes.
Now according to current scene, One Thread, may have more than one Post. Here you'll always get body and created_at in relationship for object Thread e,g Thread::with('post')->get();, and vice versa

Related

How to achieve dynamic relation with null relation allowed?

What I am trying to achieve is a polymorphic relation of a table called 'questions'
Quesions
-------------
id
title
description
type
metadata_type
metadata_id
Question can be of three types, text-type-question, choice-question, opinion-scale.
text-type-question doesn't have any relation table it only requires a basic title and description fields. but the other two requires Extra metadata which is in another two tables whose Model specified in metadata_type
What I tried to do is
class SurveyQuestion extends Model
{
public function metadata()
{
return $this->hasOne(app($this->metadata_type));
}
}
This returns me the metadata fine but my text-type-question doesn't have any relation table, ie $this->metadata_type is null. so it will throw an error.
is there any way to return a null relation by checking type condition?

Eloquent way of defining a one-to-one relationship using intermediate table to avoid foreign key cycle

Laravel rookie here. I've got titles and reviews. A title can have many reviews, a review belongs to a title.
So far, my tables look unsurprisingly as follows:
titles:
- id (pk)
...
reviews:
- id (pk)
- title_id (fk)
...
Now, I want a title to have zero or one (so-called) top reviews.
My first instinct was to add a nullable top_review_id column to the titles table, but I wanted to avoid a cycle of foreign keys. So instead, I created a third table called top_reviews as follows:
top_reviews:
- id
- title_id (fk, unique)
- review_id (fk, unique)
That way a title is guaranteed to have at most one top review and a review cannot be the top review for multiple titles. (I do realise that it is still possible to have a top review entry where the review actually belongs to a different title, but that's okay.)
My question is how do I wire that up cleanly in Laravel (7.x) ideally using Eloquent ORM relationships and following the framework's best practices?
So far I've got this:
class Title extends Model {
public function reviews() { return $this->hasMany(Review::class); }
public function topReview() { /* ??? */ }
}
class Review extends Model {
public function title() { return $this->belongsTo(Title::class); }
}
I've considered the following:
I could manually build something ugly like return Review::find(DB::table('top_reviews')->select('review_id')->where('title_id', $this->id)->get());, but I suspect there is a nicer Laravelesque way for these trivial relationships.
Simply using hasOne() doesn't seem to be the solution either since it will assume a different table name (namely reviews instead of top_reviews) and there is no way to specify a custom table.
Defining a model TopReview seems clumsy, but perhaps it is my best bet. I suppose that would allow me to define topReview() as hasOneThrough(Review, TopReview).
Feel free to correct me if I'm on the wrong track.
Thanks.
With belongsToMany relationship
class Title extends Model
{
public function reviews()
{
return $this->hasMany(Review::class);
}
public function topReview()
{
return $this->belongsToMany(Review::class, 'top_reviews', 'title_id', 'review_id');
}
}
Anyways you can skip that top_reviews table and just save top_review_id into your titles table and I think that's more efficient
Depend on you requirement
You one only one top reviews so I think you should Has One Through but if you want to have multi top reviewer on 1 title you should use Many To Many.
Has One Through
I will remove title_id if i use it
top_reviews:
- id
- review_id (fk, unique)
class Title extends Model {
public function topReview() {
return $this->hasOneThrough(TopReview::Class, Review::class);
}
}
Many To Many
top_reviews:
- id
- title_id (fk, unique)
- review_id (fk, unique)
class Title extends Model
{
public function topReview()
{
return $this->belongsToMany(Review::class, 'top_reviews', 'title_id', 'review_id');
}
}
but as you describe above. I think I will use has one through.

HasManyThrough Polymorphic model or morphto without intermediary?

Consider the following Eloquent docs polymorphic example as a starting point:
(https://laravel.com/docs/5.5/eloquent-relationships#polymorphic-relations)
In this variation, it's a bit of a reverse direction. User hasMany Medias. Medias are polymorphic and can be any model.
Goal is to be able to query User->media relation and return the corresponding post and video models.
Issue: Currently the only way I can get the post or video models, is query the mediable relation: User->media->mediable. In order to map the polymorphic models, we need the medias table where the media_id and media_type are recorded.
A HasManyThrough relation seems like the logical solution, but appears to be constrained by having to know the desired model type.
Is there a way to achieve my desired results?
DB:
user
id
posts
id - integer
title - string
body - text
videos
id - integer
title - string
url - string
medias
id - integer
user_id - integer
media_id - integer
media_type - string
Models:
class User
public function media()
{
return $this->hasMany('App\Media');
}
class Media
public function mediable()
{
return $this->morphTo();
}
class Post
public function Media()
{
return $this->morphMany('App\Media', 'mediable');
}
class Videos
public function Media()
{
return $this->morphMany('App\Media', 'mediable');
}

Eloquent: Has Many Through Relationships

I am trying to figure out a relationship but I can't seem to solve the issue.
So what my script does first is checking if there is a valid session where status = 0.
Then I want to check if there is a valid trial where status = 0 ->first() associated with that session. And if so, I want to grab all the relevant data related by trial_id.
I understand what logic is required. However, I am wondering if there is a method to do this with as little commands as possible using Eloquent relationships.
Specifically, once i have the $session object. How can I filter the trials, in order to get the appropriate stimuli_tracker data?
The important components to the relationships for the table is as follows:
Sessions
id (has one to many relationship to trials(sessions_id)
user_id (foreign key)
status
Trials
id (one to many relationship with stimuli_tracker)
sessions_id (foreign key)
status
Stimuli_Tracker
trials_id (foreign key)
stimulus
stimulus_type
Sessions Model
class Sessions extends Model
{
protected $table = 'sessions';
public function stimuliTracker()
{
return $this->hasManyThrough('App\StimuliTracker', 'App\Trials', 'sessions_id','trials_id');
}
}
Trials Model:
class Trials extends Model
{
public function stimuli()
{
return $this->hasMany(App\StimuliTracker);
}
}
EDIT
I have tried in artisan tinker to
$object = \App\Session::where(arg);
then I tried to
$object->stimulus
but didn't work. I tried a few other fields but I only received null. Maybe I'm not getting how to grab the content properly
$object->stimulus is an undefined attribute based on what you've shown in your code.
To access the stimulus information for your session, you have to use the name of the relationship, which in this case is:
$object->stimuliTracker
The thing is that this will return an Eloquent Collection because it is a hasManyThrough relationship (which is a hasMany of a hasMany).
I'm assuming that the 'stimulus' attribute belongs to the StimuliTracker class. If this is the case, then you will need to loop through your StimuliTracker Collection to extract it:
foreach ( $object->stimuliTracker as $record )
{
$stimulus = $record->stimulus;
// do something with $stimulus
}
EDIT (Added):
If you are just looking for an array of the values in the 'stimulus' attribute, you can get that with the lists() method:
$stimulus_values = $object->stimuliTracker->lists('stimulus');

Laravel 4 Create post type relationship

I'm trying to understand the eloquent ORM.
I've created a basic post blog.
I want to add post types to my posts, each post should only have one type.
Post types:
News Post
Video Post
Blog Post
Database structure:
Table: archives
id
title
content
created_at
updated_at
excerpt
deleted_at
status
Table: types
id
name
created_at
updated_at
Table: archive_type
id
archive_id
type_id
created_at
updated_at
Models:
Model: Archive
class Archive extends Eloquent
{
protected $fillable = array('title', 'content', 'excerpt');
public function type()
{
return $this->belongsToMany('Type');
}
}
Model: Type
class Type extends Eloquent
{
protected $fillable = array('name');
}
This works, when running:
Archive::with(array('type'))->orderBy('id', 'DESC')->get();
But it returns a collection, I think this is wrong because it should only return one result.
The other problem I have is how to add a new row to the archive_type database for a new post.
Any help is appreciated.
A collection is always returned whether there is zero or 1 million results. The only exception to this rule is the "find" methods, where they are intended for use as a primary key lookup method, in which case, there can only ever be zero or one results, so it will return the model it finds instead of a collection. The behaviour your experiencing with your lookup is as expected.
If you would like to return the first model, you can place ->first() after your ->get().
To create a new archive type inc. the relation you should do:
// Get our archive and type models
$archive = Archive::with(array('type'))->orderBy('id', 'DESC')->get()->first();
$type = Type::where('name','=','video')->get()->first();
// attach them!
$archive->type()->attach($type);
Edit: How to display in blade template.
To pass the collection (or any data for that matter) through to the view you do this in your controller:
return View::make('yourview')->with('archives', $archives);
To loop through a collection in a blade template use #foreach
#foreach ($archives as $archive)
<p>This is archive id is {{ $archive->id }}</p>
#endforeach
otherwise if you've passed through just one model rather than a collection you can just do
<p>This is archive id is {{ $archive->id }}</p>
If your going to ask "how do i just show the first model from a collection in the view", the simple answer is, get the model out of the collection first then pass it to your view, anything else would require "business logic" in your view.

Categories