Laravel - Model get data from another Model - laravel

I'm quite new to laravel and I'm trying to understand the Eloquent Relations.
I've already read some answers and the documentation but I haven't found a simple case similar to mine.
I have two model with one-to-many relation.
Document Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Document extends Model
{
public function dossiers()
{
return $this->belongsTo('App\Dossier');
}
protected $table = 'documents';
protected $primaryKey = 'id_document';
}
Dossier Model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Dossier extends Model
{
public function documents()
{
return $this->hasMany('App\Document');
}
protected $table = 'dossiers';
protected $primaryKey = 'id_dossier';
}
So there is an attribute "protocol" inside both table. Inside Dossier is an unique attribute, inside Document can be repeated because more documents may be part of same dossier with that protocol.
Let's suppose I have to create a Document Model. But I need an attribute "color" that already exists in a Dossier.
so:
I have a form for the document. That can set all attribute except
color (so it can create the protocol shared with Dossier)
I submit the form then check if exists a Dossier with that protocol
(that is not the primary_key)
If i find a Dossier with that protocol, I took the Color of that dossier and
I insert it in the Document Model.
I'd like to know how I should implement the second step.
At the moment I've wrote in the DocumentController:
$document = Dossier::where('protocol', '=', $request->protocol)->first();
and then
$document -> color;
But I fell that's not the way.
Thanks for any advice

I think you want to insert color in document from dossier table if protocol match. If i understand you correctly then you can write it like this.
$dossier = Dossier::where('protocol', '=', $request->protocol)->first();
$color = ($dossier) ? $dossier->color: 'defaultColor';
Now in your create document
$document -> create([
'color' => $color,
... other form data here
]);
Note: it is not good practice to use primary key field as id_document or id_dossier. Better you change these to id, otherwise your current relationship will not work. Default, laravel relationship assume id as primary key. If you want to use different primary key name then you need to pass that key name in relationship as a second parameter.
$this->hasMany('App\Comment', 'foreign_key', 'local_key');
https://laravel.com/docs/5.6/eloquent-relationships#one-to-many

There are manyway to write the relation code, one is as rkj said anad u did as well.
The other one which i personally prefer is
$this->hasManny(Comment::class,'foreign_key', 'local_key'); //This is only if you are not following laravel standard table syntex.
But if your local key is id and foreign key on another table is comment_id. Then you dont need to add foreign key and primary key. You can simply do like this
$this->hasManny(Comment::class);
For making query, You can simply do this
$dossier = Dossier::where('protocol', $request->protocol)->first(); //This will give object
You do not need = , if its other then that u need to add that. This way its looks much cleaner right?
Then to display you can simply do this $dossier->color

Related

Laravel Polymorphic Many-to-Many relationship pivot table with relationship to another Model

I have the following table structure as shown in the diagram:
Briefly, it is composed of several many-to-many polymorphic relationships as described:
many resources can have many sources and the pivot table sourceables contains catalog_number and lot_number information to make each row in the pivot table unique. Many resources could also come from the same source or from different sources, differentiated by the catalog number and lot number on the pivot table.
many resources can also have many publications attached to it, through the publicationables table with notes on the pivot table
a resource's source could also be described in many publications.
My questions:
Since the resource's source is differentiated by the pivot table sourceables how should I save the relationship between the pivot rows of sourceables to the publications?
Can you have a custom intermediate table models between both sourceables and 'publicationables' to link to the publications?
How to retrieve a resource with all it's publications and also with the sources with all corresponding publications?
Here is my answer and I hope that I can bring some light to your problem. I already publish a GitHub repository with an example of all the code I write here. I add more information about how to replicate my scenario there.
The Database and The Relations
Here is my interpretation of the Database and its relations. You can check all the Migrations on the repository.
The Solution
Question 1:
How should I save the relationship between the pivot rows of sourceable to the publications?
Answer:
Before proceeding with the code example, I would like to explain some important concepts to understand. I'm going to use the expression tag to refer to the identifier or index Morph Relations used to relate models.
The way this works, it's by assigning the tag to any Model you want to add into a relation. Any model using these tags can be store in the Morph Pivot Table. Laravel uses the _"modelable"type column to filter the call on the relations storing the Model Name. You can "tag" your Model with a Relation creating a method into the Model that returns the morphToMany relation function.
For this specific case, here's how to proceed:
In your Resource Model, you have two methods, one related to the sourceable index and the other with the publicationable tag using morphToMany in return.
Here's how it's look the Resource Model (./app/Models/Resource.php):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Resource extends Model
{
use HasFactory;
protected $guarded = [];
public function publications()
{
return $this->morphToMany(Publication::class, 'publicationable')->withPivot('notes');
}
public function sources()
{
return $this->morphToMany(Source::class, 'sourceable')->withPivot(['catalog_number', 'lot_number']);
}
}
In your Publication Model, you have two methods, one related to the sourceable index and the other with the inverse relation with the Resource Method to the publicationable tag using morphedByMany in return.
Here's how it looks the Publication Model (./app/Models/Publication.php):
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Publication extends Model
{
use HasFactory;
protected $guarded = [];
public function sources()
{
return $this->morphToMany(Source::class, 'sourceable')->withPivot(['catalog_number', 'lot_number']);
}
public function resources()
{
return $this->morphedByMany(Resource::class, 'publicationable');
}
}
With this, you can be able to accomplish your goal of relating Publications with Resources and Sources.
Question 2: Can you have an intermediate table between both sourceable and publicationable to link to the publications?
Answer:
No, you don't need to. You can use the sourceables table to accomplish this. You can always relate a Source with ANY model by creating the method that returns the morphToMany relation to the Source model. These what we do with Publications on Question 1.
Question 3: How to retrieve a resource with all its publications and the sources with all corresponding publications?
Answer:
I think Eloquent it's my favorite feature on the whole Laravel Framework. This the cherry on the cake with all we do on the Model definition.
If you check the Resource and Publication Model definition again, we add a withPivot() method with the related field we want to include on any call we do to the relation with eloquent. This method made it possible to read custom values from the Pivot table.
IMPORTANT: For this example, I'm implicitly adding the pivot values because I don't declare those columns as NULL on the migrations.
To relate (Store on the Pivot table) a publication with a resource using the relation, you just need to:
(Using artisan tinker)
Psy Shell v0.10.8 (PHP 8.0.6 — CLI) by Justin Hileman
>>> $publication = \App\Models\Publication::find(5)
>>> $resource = \App\Models\Resource::find(19)
>>> $resource->publications()->attach($publication, ["notes" => "Eureka!"]);
### Adding another Publication
>>> $publication = \App\Models\Publication::find(10)
>>> $resource->publications()->attach($publication, ["notes" => "Eureka 2!"]);
(Using a Controller)
use App\Models\Resource;
use App\Models\Publication;
...
$id_resource = 1; // This is the Resource Id you want to reach.
$id_publication = 10; // This is the Resource Id you want to reach.
$resource = Resource::find($id_resource);
$publication = Publication::find($id_publication);
$pivotData = [ "notes" => "Eureka!" ];
$resource->publications()->attach($publication, $pivotData);
To retrieve all publications from a resource, you just need to:
(Using artisan tinker)
Psy Shell v0.10.8 (PHP 8.0.6 — CLI) by Justin Hileman
>>> $resource = \App\Models\Publication::find(5)
>>> $resource->publications()->get();
Easy right? :) Eloquent POWER!
(Using a Controller)
use App\Models\Resource;
...
$id_resource = 1; // This is the Resource Id you want to reach.
$resource = Resource::find($id_resource);
$resource->publications()->get();
Just in case of any doubt, this is how you can store and retrieve from all the models:
(Using a Controller)
use App\Models\Publication;
use App\Models\Resource;
use App\Models\Source;
...
... Method ...
$id_publication = 1;
$id_resource = 1;
$id_source = 1;
$publication = Publication::find($id_resource);
$resource = Resource::find($id_resource);
$source = Source::find($id_resource);
$publicationPivotColumns = [
"notes" => "This is a note...",
];
$sourcePivotColumns = [
"catalog_number" => 100,
"lot_number" => 4903,
];
// Storing Data
// Attach (Store in the publicationables table) a Publication to a Resource
$resource->publications()->attach($publication, $publicationPivotColumns);
// Attach (Store in the sourceables table) a Source to a Resource
$resource->sources()->attach($source, $sourcePivotColumns);
// Attach (Store in the sourceables table) a Source to a Publication
$publication->sources()->attach($source, $sourcePivotColumns);
// Retraiving Data
// Get all Sources from a Resource
$resource->sources()->get();
// Get all Publications from a Resource
$resource->publications()->get();
// Get all Sources from a Publication
$publication->sources()->get();

Implementing Composite Keys in CodeIgniter Models

Is there any way you could implement composite primary keys using Models in CodeIgniter 4?
Like this?
class SomeModel extends Model{
protected $table = 'table';
protected $primaryKey1 = 'primary_composite_id1';
protected $primaryKey2 = 'primary_composite_id2';
protected $primaryKey3 = 'primary_composite_id3';
// numbers in the identifiers were only added for clarity
...
}
I think you can define the table structure using the Forge class. Here is a snippet of how I defined the table in Migrations.
in SomeMigration.php
class SomeClass extends Migration{
/* added fields here */
...
// Set them as foreign keys
$this->$forge->addForeignKey('item_id', 'item', 'item_id', 'CASCADE', 'CASCADE');
$this->$forge->addForeignKey('poster_uid', 'user', 'user_id', 'CASCADE', 'CASCADE');
$this->$forge->addForeignKey('customer_uid', 'user', 'user_id', 'CASCADE', 'CASCADE');
// Set them as primary keys
$this->$forge->addPrimaryKey('item_id');
$this->$forge->addPrimaryKey('poster_uid');
$this->$forge->addPrimaryKey('customer_uid');
...
}
Also, if setting composite primary keys in Models are not possible, should I
Create a new primary key column for that table instead?
Leave out the $primarykey value as empty and use just use queries (e.g. using WHERE)?
Use any one of the columns(set as PK) in the table as the value for $primarykey?
questions were created based on this post Codeigniter MY_Model composite primary key, since the answer did not directly answer the question
I am currently using the framework for our school project. There was none mentioned in the documentation, so I got stuck. Any kind of alternative solution is very much appreciated. Thanks! Cheers!
I have run into the same problem these days.
It seems that Codeigniter doesn't support this functionality for the model's primary key.
I searched in documentation and in Model's source code and I saw that it handles the primary key like one value.
e.g. at models find method I can see this code:
$row = $builder->whereIn($this->table . '.' . $this->primaryKey, $id)
In my case I will add an extra ID column and use this as a primary key. If I find a better alternative in the future I will update here.
Best Regards

Eloquent join with where clause

I have problems to build a relationship with eloquent.
I have two models created, Spielplan and Verein. In model Spielplan I have the fields Team_ID and Spiel_ID. In model Verein I have the field V_ID and Name. Now I need to join this two tables about Team_ID = V_ID.
This is my model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Spielplan extends Model
{
protected $table = 'Spielplan';
public function vereinFunction(){
return $this->hasOne('App\Verein', 'V_ID');
}
}
And this is a function in my web route where I want to get Spiel_ID and Name.
Route::get('test', function(){
$spieleT = App\Spielplan::where('Spiel_ID', '=', 30)->get();
foreach($spieleT as $da){
echo $da->Spiel_ID;
echo $da->vereinFunction->Name;
}
});
The first echo works and I get back Spiel_ID but the second echo gives back ErrorException Trying to get property of non-object.
What is wrong with my code?
Try editing this line:
$spieleT = App\Spielplan::with('vereInFunction')->where('Spiel_ID', '=', 30)->get();.
The with() allows you to fetch the association at the time you use get(). After using get(), you're working with a collection, and can't query the database again.
Try specifying the model primary key as a third argument, because if not, Laravel will assume it is named id, which is not the case.
Allow me to suggest you something: I used to name the tables and fields like you do (in the days I use Codeigniter) but since I started using Laravel around three years ago, I follow Laravel convention (which is recommended, but not imposed). I now name the tables in lowercase, (snakecase) plural, table fields also snakecasm lowercase. Models singular, camelcase similar corresponding table, relation function names as related model, being singular if relation is to one, plural if to many, etc. The advantage of this is among other reflected in model relationship declaration, which is a lot simpler and easier to define.
For instance (only as demonstration of stated above),
tables (with relation one to many:
managers (primarykey: id, name, ......)
technicians (primary key: id, foreingkey: manager_id (related table name in singular plus underscore plus id), name, .....)
models:
Manager:
/* relationships */
public function technicians () // see name as related table, plural due to as many relationship)
{
return $this->hasMany(Technician::class); // as naming convention has followed, you don't need to specify any extra parameters;
}
Techician:
/* relationship */
public function manager() // named as related table, singular due to to one relationship
{
$this->belongsToOne(Manager::class); // again, as naming convention has followed, you don't need to specify any extra parameters;
}
Therefore you can do this:
$manager::find(1);
echo $manager->technicians->first()->name,
or
foreach ($manager->technicians as $technician) {
echo $technician->name;
}
as well as:
$technician->manager->name;
Remember, a proper model relationship definition will save a lot of headache along the way, like the one you have
Hope this help in anyway

Laravel assumes the database table is the plural form of the model name

By default, Laravel is assuming that the database table is the plural form of the model name. But what if my table name is "news" and i still want to use this feature? should i change it to "newses" or should i use "new" for the model name?
You may specify a custom table by defining a table property on your model as below
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* The table associated with the model.
*
* #var string
*/
protected $table = 'my_flights';
}
Ref:https://laravel.com/docs/5.1/eloquent
If you have a model ending with the letter 's', it will keep the table name the same. In your case, your model will name your table news by default.
If you want to use another name though, you can use:
protected $table = 'tablename';
inside of your model.
EDIT: I tested this in my application. I made a model named News. Then I made a new instance of News and retrieved the table name:
$news = new News();
dd($news->getTable());
It returns: news
Inside your eloquent model you have to define table name. For example if my model is named user and table in database is named user_of_application then i do it this way
class user extends Model
{
protected $table = 'user_of_application';
}
Laravel uses a "standard set" rule that defines:
A Model is a single instance of a record
A Collection is one or more records
Therefore it assumes that a table is a collection of records.
The nomenclature has a problem when it clashes with various features / gotchas of human languages. For example, what if I have a Model called Sheep? That does mean my Database Table should be called "Sheeps"?
It's up to the developer to avoid "Gollum/Smeagol" syntax. Indeed, you wouldn't want a table called "Newses" as much I'd like to end up with a table called "Sheeps".
Ultimately, I construct Migrations with:
sudo php artisan make:migration create_sheep_table --create=sheep
As for Models, you'll notice in the documentation that they have a different table name for "Flights" called "my_flights"
https://laravel.com/docs/master/eloquent#defining-models
Again, it's up to the developer / DB manager to make decisions on naming conventions that make sense in an application context.

Laravel 4 Use the same Model and Controller for 12 lists for selects

I have to meke models, controllers and views for 12 tables. They have all the same structure id, name, order.
I was thinking and maybe using:
Controller
index($model)
$model::all()
return View::make(all_tables,compact('model'))
edit($model,$id)... and so on.
But and don't know if there's a way for using only one model.
Did anybody do anything like this?
Any idea?
Thanks
Although each model has the same table structure, what you're trying to achieve would not be advisable as you'd lose a lot of the fluent capabilities of Laravel's Eloquent ORM.
Regarding the controller, this would work:
<?php
namespace App\Http\Controllers;
use App\Http\Requests;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class GenericModelController extends Controller
{
public function loadModelById($model, $id)
{
$instance = \App::make('App\\' . ucfirst($model));
return $instance->find($id);
}
}
You'll need the following route:
Route::get('show/{model}/{id}', 'GenericModelController#loadModelById');
Example, to load a user with an id of 1:
http://www.yourdomain.com/show/user/1
Edit: I just saw that you're using Laravel 4, so the syntax for defining a route will be a little different I believe but the general concept will still work. Testing in Laravel 5 and works perfectly.
You should get get some idea from here.Please use the link below.
https://scotch.io/tutorials/a-guide-to-using-eloquent-orm-in-laravel
// app/models/Bear.php
class Bear extends Eloquent {
// MASS ASSIGNMENT -------------------------------------------------------
// define which attributes are mass assignable (for security)
// we only want these 3 attributes able to be filled
protected $fillable = array('name', 'type', 'danger_level');
// DEFINE RELATIONSHIPS --------------------------------------------------
// each bear HAS one fish to eat
public function fish() {
return $this->hasOne('Fish'); // this matches the Eloquent model
}
// each bear climbs many trees
public function trees() {
return $this->hasMany('Tree');
}
// each bear BELONGS to many picnic
// define our pivot table also
public function picnics() {
return $this->belongsToMany('Picnic', 'bears_picnics', 'bear_id', 'picnic_id');
}
}
I find a simple way.
Only one model, one controller and one view(index,edit, etc) too.
A single table with
id, name of list, value (name to appears in the list)
Yo pass can pass to de view all the values per list, and for any list in the table you can create de select if it's no empty.

Resources