Laravel polymorphic relation in Nova - laravel

My current blog like app has a Page that could have many Paragraphs of a different structure (Text, Image, File download, Registration form, ... ). When trying to translate this to Eloquent Models with relations, I think this is an easy way to do so:
Table pages:
id
title, ...
Table paragraphs:
id
page_id
paragraphable_id
paragraphable_type
Table paragraph_texts:
id
text
Table paragraph_images:
id
image_path
Table paragraph_downloads:
id
file_path
And the Models:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
public function paragraphs() {
return $this->hasMany(Paragraph::class);
}
}
?>
The Paragraph Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Paragraph extends Model
{
public $table = 'paragraphs';
public $timestamps = [];
public function page() {
return $this->belongsTo(Page::class);
}
public function paragraphable() {
return $this->morphTo();
}
}
The Text Type model as sample:
<?php
namespace App\Models\ParagraphTypes;
use Illuminate\Database\Eloquent\Model;
class Text extends Model
{
protected $table = 'paragraph_texts';
public function paragraph() {
return $this->morphOne(Paragraph::class, 'paragraphable');
}
}
I now want to view the page in nova and create a new paragraph by a selectable type. I created the Resource classes (for page, for paragraph and for each paragraph type) according to the eloquent relations (HasMany field for the page, MorphTo for the paragraph and just a TextArea for the Text type). When I see the details of a page and want to add a new paragraph, I can see the form to add a paragraph and can select the paragraph type in a dropdown but can only see the already existing entries, not new one. I will never want to add an existing paragraph type like this.
So questions:
Is the transformation of the structure into eloquent correct, are there any improvements?
How could laravel nova handle the creation of a new paragraph type for a page "through" the paragraph model? I feel like having to add a custom field to do so, that maybe asks for the type to create first...

Regarding your first question: You should use the relationships from the documentation:
class Page extends Model
{
public function texts() {
return $this->morphedByMany(Text::class, 'paragraphable', 'paragraphs');
}
}
class Text extends Model
{
public function pages() {
return $this->morphToMany(Page::class, 'paragraphable', 'paragraphs');
}
}
You can get multiple paragraph types with an accessor:
class Page extends Model
{
public function getParagraphsAttribute() {
return $this->texts->toBase()->merge($this->files)->merge([...]);
}
}
$paragraphs = $page->paragraphs;

Related

Laravel many to many relationship with pivot

I'm using Laravel Filament.
I got a projects and responsibles tables in a many-to-many relationship. But also another table of responsabilityTypes
projects
id
title
responsibles
id
name
responsabilityTypes
id
name
project_responsible
project_id
responsible_id
responsibilityType_id
And here are my relationships setup:
Responsible.php
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType_id')
->withTimestamps()
->using(AcademicoProyecto::class);
}
Project.php
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType_id','sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
I have set up a class for the pivot table like so:
ProjectResponsible.php
use Illuminate\Database\Eloquent\Relations\Pivot;
class AcademicoProyecto extends Pivot
{
}
ResponsibilityType.php
//Don't know how to set up
My question is, when the user is in a Project Edit page and clicks on the "attach" button, in order to add a Responsible record, a Modal pops up to select a Responsible, but I also implemented a Select list to display the different types of responsibilities.
What am I missing to set up in order to access and display the types of responsibilities in the select list and attach it to the pivot table?
Your question asks about "access and display" but you have no controller or view code. But for the model, it's just a simple relationship between two tables, so define it as such:
class AcademicoProyecto extends Pivot
{
use SoftDeletes;
public function responsibilityType() {
return $this->belongsTo(ResponsibilityType::class);
}
}
class ResponsibilityType extends Model
{
protected $fillable = ["name"];
}
Now you simply update the other models to access the relationship in the withPivot() call.
class Responsible extends Model {
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
class Project extends Model {
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType', 'sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
Now you should be able to do, for example:
$foo = Responsible::with("projects")->first();
foreach ($foo->projects as $project) {
echo $project->pivot->responsibilityType?->name;
}

How to get all records(related and non related) of Laravel belongsTo relationship?

How can I get all the records from the relationship? I mean not only the related records, but also the rest.
Let's say I have a post which belongs to some category. If I want to change the category of this post I need a list of all available categories. Can I get this list from the relationship?
The Post model:
class Post extends Model
{
public function category()
{
return $this->belongsTo('App\Category');
}
}
The Category model:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
}
In the PostsController I tried:
$postModel->category->find('all'); // Returns null
$postModel->category->all(); // Returns only the related categories
I know I can simply use the Category model in the PostsController, but I prefer to do it using the relationship.
If you feel you must use the relationship to get to the other model you could try:
$categories = $post->category()->getRelated()->get();

Eloquent one to many relationship not working

I'm stuck for hours with one of those issues where a fresh set of eyes might help. I just can't understand what's missing.
I'm connecting a model called User_ativo and defining two one-to-many relations to models Instituicao and Tipo_Ativo.
My database is simple.
Table user_ativo has columns "tipo_ativo_id" and "instituicao_id". I have a test row where both are set to 1. Both my tables instituicoes and tipo_ativos have only "id" and a string field "nome" (name). Both have a record with id == 1.
User_ativo.php:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User_ativo extends Model
{
public function tipo(){
return $this->belongsTo('App\Tipo_ativo');
}
public function instituicao(){
return $this->belongsTo('App\Instituicao');
}
}
Instituicao.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Instituicao extends Model
{
protected $table = 'instituicoes';
public function user_ativos(){
return $this->hasMany('App\User_ativo');
}
}
Tipo_ativo.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tipo_ativo extends Model
{
protected $table = 'tipo_ativos';
public function user_ativos(){
return $this->hasMany('App\User_ativo');
}
}
My controller method that fetches the date goes as follow:
public function index()
{
$ativos = User_ativo::with('tipo', 'instituicao')->get();
return view('ativos.index', compact('ativos'));
}
Now here's where it gets interesting, for some reason I can't figure out, when I echo the $ativos variable in my view I get this:
[{"id":1,"user_id":1,"instituicao_id":1,"tipo_ativo_id":1,"tipo":null,"instituicao":{"id":1,"nome":"Banco do Brasil"}}]
So, weirdly my relationship with the Instituicao model works, but the one with Tipo_ativo returns null.
I'm pretty confident someone will point out some dumb and obvious mistake in all of this, but I can't for the life of me understand why one works and the other doesn't since they're pretty much the same thing.
Your relationships names are not according to laravel convention.
Read below function and provide foreign_key and local_key/owner_key to your relationships then it will work
public function belongsTo($related, $foreignKey = null, $ownerKey = null, $relation = null){}
If we do not follow laravel convention while creating relationships then we have to tell it that these are the foreign and local keys that should be used.
Read more here
class User_ativo extends Model{
public function tipo(){
return $this->belongsTo('App\Tipo_ativo','user_ativo_id'); //second parameter is foreign_key_of_User_avito_table_here
}
public function instituicao(){
return $this->belongsTo('App\Instituicao','user_ativo_id'); //second parameter is foreign_key_of_User_avito_table_here
}
}
class Instituicao extends Model
{
protected $table = 'instituicoes';
public function user_ativos(){
return $this->hasMany('App\User_ativo','instituicao_id'); //second parameter is foreign key of Instituicao model
}
}
class Tipo_ativo extends Model
{
protected $table = 'tipo_ativos';
public function user_ativos(){
return $this->hasMany('App\User_ativo','tipo_ativo_id'); //second parameter is foreign key of Tipo_ativo model.
}
}

Need to know how to define two fields refering to same parent table in model in Laravel

In banner table I have another field image_id which also refers to media gallery , I need to know how would I define this Banner Model , as I already have defined video_id belongsTo mediagallery. I need help on this
//********** Banner model ***************************
namespace App;
use Illuminate\Database\Eloquent\Model;
class Banner extends Model
{
//
protected $table='banners';
public function mediagallery()
{
return $this->belongsTo('App\Mediagallery', 'video_id','id'); // 2nd foreign key field name , 3td is parent table primary key field name
}
}
//*********** Mediagallery model *********************
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Mediagallery extends Model
{
//
protected $table='mediagalleries';
public function banner()
{
return $this->hasOne('App\Banner', 'video_id', 'id'); // 2nd foreign key o the child table , 3td is parent or local table
}
}
Create 2 relationships. One relationship for videos and another for images.
public function videoGallery()
{
return $this->belongsTo('App\Mediagallery', 'video_id','id');
}
public function imageGallery()
{
return $this->belongsTo('App\Mediagallery', 'image_id','id');
}
I don't know the specifics of the app, hopefully this helps.

Laravel: One to many and many to many relationship on one model

So let's assume we have two Models based on our tables (Post and Tag)
The Post has a (one to many) relationship to have a so called Highlighted Tag field which only accepts one tag.
The Post has also a (many to many) relationship to have tags assigned to that post, So it's just like normal tags.
As far as I understand, You can not have more than one relationship assigned to the same Table, How is this possible using Laravel? What is the best practice?
If I understand you correctly you want that a post has a main(highlighted tag) tag and multiple normal tags. This is pretty easy.
Your Post model functions:
public function tag()
{
//Your highlighted tag
return $this->belongsTo(Tag::class);
}
public function tags()
{
//All normal tags
return $this->hasMany(Tag::class);
}
And these are the columns of your tables:
posts table:
id: int
title: string
content: string
tag_id: int
tags table:
id: int
name: string
post_id: int
You can do following :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function tags(){
return $this->hasMany('App\Tag');
}
public function highlightedtag(){
return $this->tags->where('tag_type', 'highlighted')->first();
}
}
?>
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
public function posts(){
return $this->hasMany('App\Post');
}
}
?>

Resources