Planning out Eloquent relationships in Laravel 5 - laravel-5

I have a Project model. Each Project can have a thumbnail, main image and other multiple assets / images (project details for example) and also multiple text contents (that would be placed in between these images). And each Project page will pull in these assets (except the thumbnail) and texts, and display them in a specific order (that I define by drag and drop on my control panel).
So basically the similar structure to what Behance uses for example. I'm trying to structure this using Eloquent and here is what I was thinking of doing:
I can have let's say Asset and Text models with hasMany() relationship on Project model. These two models would have a column called pageSort and then the controller would merge these two collections together and sort them using that pageSort column.
Any other suggestions?
EDIT:
I tried to use the polymorphic relationships in Laravel, but no success yet.
MyProject model:
public function nodes()
{
return $this->morphedByMany(Node::class, 'nodeable');
}
My Node model:
public function nodeable()
{
return $this->morphTo();
}
Then in my code I do the following:
$project = new App\Project;
$project->title = 'Some title';
$project->save();
$image = new App\Image;
$image->source = 'somefile.jpg';
$project->nodes()->save($image);
But I get the following error:
Illuminate\Database\QueryException with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'project_id' in 'field list' (SQL: insert into `nodeables` (`nodeable_id`, `nodeable_type`, `project_id`) values (5, App\Node, 1))'
So I guess it's trying to create an entry inside a pivot table nodeables. Then I created this table and it saved an entry inside the pivot table, but not inside nodes table.
This is driving me mad! :(

I would have a Node model that is polymorphic, and has your sort_order column. A Project would have many Node models, and a Node could be morphed to whatever you need: Text, Image, Video, and so on.
class Project extends Model
{
public function nodes()
{
return $this->hasMany(Node::class)->orderBy('sort_order');
}
}
class Node extends Model
{
public function nodeable()
{
return $this->morphTo('nodeable');
}
public function project()
{
return $this->belongsTo(Project::class);
}
}
// Example
class Text extends Model
{
public function node()
{
return $this->morphOne(Node::class);
}
}

Related

Create new entry (One to One Polymorphic relationship)

Premise
I am creating a small app with User profiles that can host a list of items of different types.
Each Item can be a Link, Card, Video. So each layout can be optimized accordingly. A bit like Tumblr, if you like.
The setup
For this reason, I've opted to use a one-to-one a polymorphic relationship (maybe overkill?), with an items table that has additional columns like position (so that a user can reorder the items).
The items table:
id
user_id (foreign key)
itemable_id
itemable_type
position
...
My models look like:
Item model
public function itemable()
{
return $this->morphTo();
}
public function user()
...
Link model
public function item()
{
return $this->morphOne('\App\Item', 'itemable');
}
public function user()
...
Card model
public function item()
{
return $this->morphOne('\App\Item', 'itemable');
}
public function user()
...
…and so on for the other Item types.
Finally, in ItemController, I'm creating a dummy Link for debug purposes:
// ...
$newLink = new Link();
$newLink->url = 'example.org';
$newLink->title = 'Example Website NEW';
$newLink->user_id = $user->id;
$user->items()->save($newLink);
// ...
But, unlike how I expected, the new entry in the items table doesn't get created. I have to that myself instead, manually once I've stored the $newLink Link.
The question
Is this normal? I would have expected to have Link (with itemable_id and itemable_type created automatically.
If so, I'm definitely missing something here. How can I make Laravel create the items entry automatically?
use associate so it will be like below:
$user->items()->associate($newLink);

Laravel Create multiple records in Pivot table

I'm trying to create a function in our Laravel 5.8 app that would add multiple records to a pivot table. At present we have the following setup;
Users
Training Courses
Users Training Courses (pivot table for the above relationships, with a few extra fields)
I want to be able to show all users in the database, then check their name, pick a training course and hit "Add" and it'll create a record in the pivot table for each user that was selected.
I can't figure out where to start with this - it seems like I need to have a "for each user selected, run the store function" loop in the controller, but I have no idea where to start.
I wasn't sure if there was an easy way to do this in eloquent or not. Is there a simple way to do this?
Eloquent does this automatically if you set up the relationships correctly and you don't have to worry about pivot tables.
class Users
{
public function trainingCourses()
{
return $this->hasMany(TrainingCourses::class);
}
}
class TrainingCourses
{
public function user()
{
return $this->belongsTo(User::class);
}
}
Then you can use the save() method to create the relationship. But I find it better to wrap this function inside a helper method that you can use throughout your code:
class Users
{
...
public function assignTrainingCourse(TrainingCourse $trainingCourse)
{
return $this->trainingCourses()->save($trainingCourse);
}
}
In your code, you could then do something as simple as this:
$user = User::find(1);
$trainingCourse = TrainingCourse::find(1);
$user->assignTrainingCourse($trainingCourse);
Building on this, suppose you have the following route to assign a training course, where it expects a trainingcourse_id in the request:
Route::post('/users/{user}/trainingcourses', 'UserTrainingCoursesController#store');
Thanks to route model binding, Laravel can inference the parent model (user) from the URL, and your controller might look like this:
// UserTrainingCoursesController.php
public function store(User $user)
{
$trainingCourse = TrainingCourse::find(request()->input('trainingcourse_id'));
$user->assignTrainingCourse($trainingCourse);
return back();
}
Of course, you'll want to put some validation in here, but this should get you started.

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

Laravel Eloquent Nested Relationships. Storing rows in controller variable

I have some working queries that are not ideal and I'd like to try perform them the Eloquent way to tidy it up.
I think I've got the relationships defined correctly but I can't figure out how to write the queries and store them in variables in the controller. I need to return these variables back to the view because I json encode them for use in JavaScript.
Here's what I have in my controller:
public function show($idProject)
{
$project = ProjectsModel::with('user')->where('idProjects','=',$idProject)->first();
$objsettings = ObjSettingsModel::where('idObjSettings','=',$project['ObjSettingsID1'])->first();
$obj = ObjModel::where('idObjs','=',$objsettings['Objs_idObjs'])->first();
return view('project')->with('project',$project)->with('obj',$obj)->with('objsettings',$objsettings);
}
The naming conventions are a bit off so here's what this does.
I pass the $idProject to the controller from a link on my index page where I've looped through and paginated all rows from the Projects table.
The first query finds the project row where it's id (idProjects) matches the variable passed from the index page via the link (idProject). I've also successfully pulled the related user row from the user table using an eloquent relationship.
The next query pulls from an ObjSettings table which stores a number of settings values for an object which is shown on the page. I match the idObjSettings column of the ObjSettings table to the previously pulled $project['ObjSettingsID1'] which is essentially the foreign key in the projects table. There can be several ObjSettings for each Project.
The 3rd query pulls from the Obj table which stores details about an object. These are static details on objects such as name or size for example. I match the idObjs column to the previously pulled $objsettings['Objs_idObjs'] which is the foreign key in the ObjSettings table. One Obj can have many ObjSettings which are used in other Projects.
Here's how I'm passing the php variables to JS:
<script>var obj = #json($obj);</script>
<script>var objsettings = #json($objsettings);</script>
Here are my relationships
ProjectsModel
public function user()
{
return $this->belongsTo('App\User', 'Username', 'id');
}
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjSettings', 'ObjSettingsID1' );
}
ObjSettingsModel
public function objs()
{
return $this->belongsTo('App\ObjsModel', 'Objs_idObjs', 'idObjs');
}
public function projects()
{
return $this->belongsTo('App\ProjectsModel', 'ObjSettingsID1', 'idObjSettings' );
}
ObjModel
public function objsettings()
{
return $this->hasMany('App\ObjSettingsModel', 'idObjs', 'Objs_idObjs');
}
I've tried a whole range of queries such as:
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
$obj = $project->objsettings->objs;
but I keep running into issues such as "Property [objs] does not exist on this collection instance." I suppose I'm returning multiple rows in this case? Any help would be appreciated.
You need to loop through objsettings
$project = ProjectsModel::with('user')->with('objsettings.objs')->where('idProjects','=',$idProject)->first();
$objsettings = $project->objsettings;
foreach($objsettings as $objsetting){
$objs = $objsetting->objs;
}

Join 3 tables using Laravel Eloquent

So I have 3 tables
size_sets - id, name
sizes - id, name
size_set_sizes - size_id, size_set_id
I want to define a relationship in size_set model that would retrieve all sizes available for that sizeset
Something like:
public function sizes()
{
//define relationship here
}
Method sizes should retrieve the names from the size table, through size_set_sizes table in the size_set model...
My application is very dynamic and thus I needed to go with this structure. I tried the hasManyThrough relationship, but couldn't get that to work.
100% use a pivot table
https://laravel.com/docs/5.4/eloquent-relationships
This link will give you all you need
Use belongsToMany() relations like:
class Size extends Model
{
public function sizeSets()
{
return $this->belongsToMany(SizeSet::class, 'size_set_sizes');
}
}
class SizeSet extends Model
{
public function sizes()
{
return $this->belongsToMany(Size::class, 'size_set_sizes');
}
}
Then you can do:
$sizeSet = SizeSet::with('sizes')->find($id);
Then $sizeSet->sizes will return a collection of sizes for that size set.
I Think I found what I was looking for The answer is a pivot-table
http://laraveldaily.com/pivot-tables-and-many-to-many-relationships/

Resources