I am building a timesheet system and have setup a model for timesheets. Timesheet can have many rows - for example when I add a timesheet, I can add many days (rows) to the timesheet.
I want to be able to sync rows when a timesheet gets saved. For example, new rows will be added to the database, missing rows from the given array will be removed from the database.
I understand I can use sync method which works like this, however, I do not think I need a belongsToMany relationship. Currently I have my row relationship setup as a hasMany. The timesheet model looks like this:
<?php
namespace App\Models\Timesheet;
use Illuminate\Database\Eloquent\Model;
class Timesheet extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'timesheet';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['user_id', 'week', 'year', 'token', 'total_hours'];
/**
* Define that we want to include timestamps.
*
* #var boolean
*/
public $timestamps = true;
/**
* Boot the model.
*
*/
public static function boot()
{
parent::boot();
static::deleting(function($timesheet)
{
$timesheet->row()->delete();
});
}
/**
* The rows that belong to the timesheet.
*
* #return Object
*/
public function row()
{
return $this->hasMany('App\Models\Timesheet\RowTimesheet');
}
}
The row_timesheet model looks like this:
namespace App\Models\Timesheet;
use Illuminate\Database\Eloquent\Model;
class RowTimesheet extends Model
{
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'row_timesheet';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['timesheet_id', 'activity_category', 'description', 'eri_number', 'ewn_number'];
/**
* Define that we want to include timestamps.
*
* #var boolean
*/
public $timestamps = true;
What do I need to do in order to make something like this work:
$this->timesheet->find($id)->row()->sync($data);
Thanks in advance.
I believe the 'sync' methods works with 'belongsTomany' relationship.
what you have is 'hasMany' relationship, for that you need to do something like below
use 'save' method instead of 'sync' for hasMany relationship
$data = new App\Comment(['message' => 'A new comment.']);
$this->timesheet->find($id)->row()->save($data); // saves single row sheet object for a timesheet
$this->timesheet->find($id)->row()->saveMany($multipleData); // saves multiple row sheet objects for a timesheet
Related
need some light on a problem... I'm trying to get data from another database using a Many-To-Many relation.
Basically, a site can have many templates and a template can have many sites.
Site Model:
class Site extends Model
{
use HasFactory;
/**
* Database Connection Name
*/
protected $connection = 'hub';
/**
* Model Table Name
*/
protected $table = 'tbl_sites';
/**
* Model Primary Key
*/
protected $primaryKey = 'id';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'code', 'name', 'abbreviation', 'address', 'zipcode', 'town', 'geolocation_id', 'gps'
];
/**
* Returns associated SGC templates
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function sgc_templates()
{
return $this->belongsToMany('App\Models\SGC\Contracts\Templates\Template', 'sgc_contracts_templates_hasmany_sites', 'site_id', 'template_id');
}
}
Template Model:
class Template extends Model
{
use HasFactory;
/**
* Database Connection Name
*/
protected $connection = 'sgc';
/**
* Model Table Name
*/
protected $table = 'sgc_contracts_templates';
/**
* Model Primary Key
*/
protected $primaryKey = 'id';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'name', 'description', 'file_name'
];
/**
* Returns associated sites
*
* #return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function sites()
{
return $this->belongsToMany('App\Models\Hub\Sites\Site', 'sgc_contracts_templates_hasmany_sites', 'template_id', 'site_id');
}
}
If I try to get templates associated to a site with: Site::with('sgc_templates')->find(1), everything works fine.
If I try to get sites associated to a template with: Template::with('sites')->find(1), I got error. Basically saying that the pivot table doesn't exists on sites database. The templates and the pivot table are on sgc connection/database.
The error is:
Illuminate\Database\QueryException
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'hub.sgc_contracts_templates_hasmany_sites' doesn't exist (SQL: select `tbl_sites`.*, `sgc_contracts_templates_hasmany_sites`.`template_id` as `pivot_template_id`, `sgc_contracts_templates_hasmany_sites`.`site_id` as `pivot_site_id` from `tbl_sites` inner join `sgc_contracts_templates_hasmany_sites` on `tbl_sites`.`id` = `sgc_contracts_templates_hasmany_sites`.`site_id` where `sgc_contracts_templates_hasmany_sites`.`template_id` in (1))
Clearlly that the Template::with('sites')->find(1) is going to the wrong database, because on the error, 'hub.sgc_contracts_templates_hasmany_sites' should be 'sgc.sgc_contracts_templates_hasmany_sites'.
Can someone help me with this? :|
Thanks
Found an workaround. Seems that Many-To-Many only works in 1 direction (?).
Github Issue
Workaround
Thanks for all the help.
You need to tell Eloquent that you want to use other db, try something like this
return $this->belongsToMany('App\Models\Hub\Sites\Site', 'sgc.sgc_contracts_templates_hasmany_sites', 'template_id', 'site_id');
and then check if it tries to query sgc db.
If it still don't help try this https://stackoverflow.com/a/60060726/7892040.
I'm experiencing my first Laravel project and I implemented a resource collection API, where I fetch data via passport. Data seems to be retrieved correctly from model, except for relations. Here's the situation:
item.php (Model)
<?php
// Definizione Namespace
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* Classe Item
*/
class Item extends Model
{
use SoftDeletes;
// Dichiarazione Proprietà
protected $table = 'item';
protected $dateformat = 'Y-m-d';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = [
'data_acquisto',
'labeled',
'estensione_garanzia',
'stato',
'data_dismissione',
'note'
];
/**
* The attributes that should be hidden for arrays.
*
* #var array
*/
protected $hidden = [
'codice',
'serial',
'componente_id',
'tipologia_id',
'condizione_id',
'locazione_id',
'fornitore_id',
'parent_id'
];
/**
* The attributes that should be mutated to dates.
*
* #var array
*/
protected $dates = [
'data_acquisto',
'data_dismissione',
'deleted_at'
];
/**
* All of the relationships to be touched.
*
* #var array
*/
protected $touches = [
'componenti',
'condizioni',
'fornitori',
'locazioni',
'tipologie'
];
/**
* Scope query item figli
* Getter
* #param array $query Query
* #return array Query
*/
public function scopeFigli($query)
{
return $query->where('parent_id', '!=', null);
}
/**
* Componenti Correlati
* Getter
* #return object Componenti
*/
public function componenti()
{
// Definizione relazione
return $this->belongsTo('App\Componente');
}
/**
* Condizioni Correlate
* Getter
* #return object Condizioni
*/
public function condizioni()
{
// Definizione relazione
return $this->belongsTo('App\Condizione');
}
/**
* Fornitori Correlati
* Getter
* #return object Fornitori
*/
public function fornitori()
{
// Definizione relazione
return $this->belongsTo('App\Fornitore');
}
/**
* Locazioni Correlate
* Getter
* #return object Locazioni
*/
public function locazioni()
{
// Definizione relazione
return $this->belongsTo('App\Locazione');
}
/**
* Tipologie Correlate
* Getter
* #return object Tipologie
*/
public function tipologie()
{
// Definizione relazione
return $this->belongsTo('App\Tipologia');
}
}
item.php (Resource)
<?php
// Definizione Namespace
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Http\Resources\Componente as ComponenteResource;
use App\Http\Resources\Condizione as CondizioneResource;
use App\Http\Resources\Fornitore as FornitoreResource;
use App\Http\Resources\Locazione as LocazioneResource;
use App\Http\Resources\Tipologia as TipologiaResource;
class Item extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
parent::toArray($request);
return [
'id' => $this->id,
'codice' => $this->codice,
'data_acquisto' => $this->data_acqisto,
'serial' => $this->serial,
'labeled' => $this->labeled,
'estensione_garanzia' => $this->estensione_garanzia,
'stato' => $this->stato,
'data_dismissione' => $this->data_dismissione,
'note' => $this->note,
'parent_id' => $this->parent_id,
// Includi associazioni se caricate
'componenti' => ComponenteResource::collection($this->whenLoaded('componenti')),
'condizioni' => CondizioneResource::collection($this->whenLoaded('condizioni')),
'fornitori' => FornitoreResource::collection($this->whenLoaded('fornitori')),
'locazioni' => LocazioneResource::collection($this->whenLoaded('locazioni')),
'tipologie' => TipologiaResource::collection($this->whenLoaded('tipologie'))
];
}
}
This is the screen about an example of data fetched:
As showed above there's no trace of relations. By googling around and changing code as suggested like this:
// Resoruce - Straight including relations instead of lazy load
[...]
'componenti' => ComponenteResource::collection($this->componenti),
[...]
or by expliciting the foreign key in model:
/**
* Componenti Correlati
* Getter
* #return object Componenti
*/
public function componenti()
{
// Definizione relazione
return $this->belongsTo('App\Componente', 'componente_id');
}
I'm still not retrieving relations.
Could anyone give me a little help/tip to solve this problem?
Thanks in advance for help.
The code below will only show Tipologie when it is explicitly loaded to avoid N+1 query problems.
'tipologie' => TipologiaResource::collection($this->whenLoaded('tipologia'))
To load Tipologie for Resource to show it, you need to explicitly load it as:
$itemResource = new ItemResource($item->load('tipologia', ... other relationships...);
See Eager Loading for more information about this.
Edit
Sorry for not understanding the type of relationship, just like #luca-cattide said, collection should not be used for belongsTo, and the correct one is to use:
TipologiaResource::make($this->tipologia);
Or also:
new TipologiaResource($this->topologia);
But I advise you to use "load" method to load the information before, otherwise you perform a search in the database for "item", another by "typologie" and so on until loading all your relationships.
There's another way you load information without having to load the item, see below:
new ItemResource(App\Item::find(1)->with(['tipologie', ... other relationships ... ])->get());
See more about N+1 query problems here.
Thanks #vinicius, but googling around a bit more, as suggested from this post by #CamiloManrique, I noticed that in these relations, I'm trying to fetch data from belongs_to side (so actually from Item and not from Componente, Tipologia and so on). As is ::collection simply doesn't work except if called by hasMany relation side
So, instead using ::collection in conjunction with whenLoaded I refactored like this:
// Includi associazioni se caricate
'componente' => ComponenteResource::make($this->componente),
'condizione' => CondizioneResource::make($this->condizione),
'fornitore' => FornitoreResource::make($this->fornitore),
'locazione' => LocazioneResource::make($this->locazione),
'tipologia' => TipologiaResource::make($this->tipologia)
In this way data being fetched with no error.
Thanks again for your tips.
I try to use scaffold from table of relational-object database using infyom but i can't manipulate object like in POO because the models generated contains only the id of the other object! there is a way to manipulate object correctly?
model generated is like:
<?php
namespace App\Models;
use Eloquent as Model;
/**
* Class Facture
* #package App\Models
* #version July 17, 2018, 6:08 pm UTC
*
* #property \Illuminate\Database\Eloquent\Collection Consomme
* #property \Illuminate\Database\Eloquent\Collection contient
* #property \Illuminate\Database\Eloquent\Collection EstFactureBst
* #property \App\Models\EstFacture estFacture
* #property string num_facture
* #property date date_facture
* #property string etat_facture
* #property integer num_releve
*/
class personnel extends Model
{
public $table = 'personnel';
public $timestamps = false;
public $sousPersonnel;
protected $primaryKey = 'id_pers';
public $fillable = [
'id_pers',
'lib_pers'
];
/**
* The attributes that should be casted to native types.
*
* #var array
*/
protected $casts = [
'lib_pers' => 'string'
];
/**
* Validation rules
*
* #var array
*/
public static $rules = [
];
public function SousPersonnel(){
return $this->hasMany(\App\Models\TypePersonne::class);
}
}
there is a relation between my class personnel and TypePErsonne but when I want to write like $personnel->typePersonne and make dd I have it null I use Eloquent ORM it seems like they can't find typePersonne for my object $personnel
You have defined relation within the method named SousPersonnel() so to read it you should use SousPersonnel attribute like this:
$personnel->SousPersonnel
I have 3 Models(each associated with a table separately) which associated with each other I have attached the table structure below
Models are,
Doctor Model associated with doctor_profile_master
<?php
namespace App\TblModels;
use Illuminate\Database\Eloquent\Model;
class Doctor extends Model
{
/**
* #var string
*/
protected $table = 'DOCTOR_PROFILE_MASTER';
/**
* #var string
*/
protected $primaryKey = 'doctor_profile_master_id';
/**
* #var array
*/
protected $fillable = ['doctor_id', 'user_master_id', 'doctor_first_name', 'doctor_last_name', 'doctor_isactive'];
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function hospitalDoctorAssociateMasters(){
return $this->hasMany('App\TblModels\HospitalDoctorAssociateMaster','doctor_profile_master_id');
}
}
HospitalDoctorAssociateMaster Model associated with hospital_doctor_associate_master
<?php
namespace App\TblModels;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
class HospitalDoctorAssociateMaster extends Model
{
/**
* #var string
*/
protected $table = 'HOSPITAL_DOCTOR_ASSOCIATE_MASTER';
/**
* #var string
*/
protected $primaryKey = 'hospital_doctor_associate_master_id';
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function doctor(){
return $this->belongsTo('App\TblModels\Doctor','doctor_profile_master_id');
}
/**
* #return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function hospital(){
return $this->belongsTo('App\TblModels\Hospital','hospital_profile_master_id');
}
}
HospitalDoctorRecurringSchedule Model associated with hospital_doctor_recurring_schedule
<?php
namespace App\TblModels;
use Illuminate\Database\Eloquent\Model;
class HospitalDoctorRecurringSchedule extends Model
{
/**
* #var string
*/
protected $table = 'HOSPITAL_DOCTOR_RECURRING_SCH_MASTER';
/**
* #var string
*/
protected $primaryKey = 'hospital_doctor_recurring_sch_master_id';
public function hospitalDoctorAssociateMaster(){
return $this->belongsTo('App\TblModels\HospitalDoctorAssociateMaster','hospital_doctor_associate_master_id');
}
}
Thing i want to do is,
How to retrieve the hospital_doctor_recurring_sch_master table data using specific doctor_id(doctor_profile_master)
I tried some methods but cant able to retrieve those values.
Thanks in advance.
You could use something like this:
$hospitalDoctors = HospitalDoctorRecurringSchedule::with(['hospitalDoctorAssociateMaster', 'hospitalDoctorAssociateMaster.doctor', 'hospitalDoctorAssociateMaster.hospital'])->all();
To search by fields in related tables:
$hospitalDoctors = HospitalDoctorRecurringSchedule::with([
'hospitalDoctorAssociateMaster',
'hospitalDoctorAssociateMaster.doctor',
'hospitalDoctorAssociateMaster.hospital'])
->whereHas('hospitalDoctorAssociateMaster.doctor', function ($query) use ($doctorId) {
$query->where('doctor_id', '=', $doctorId);
})
->all();
For hospitalDoctorAssociateMaster.hospital:
Define Hospital Model and try
Trying to get data from multiple nested relationship with a where constraint:
Model User:
<?php
use Illuminate\Auth\UserInterface;
use Illuminate\Auth\Reminders\RemindableInterface;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Zizaco\Entrust\HasRole;
class User extends BaseModel implements UserInterface, RemindableInterface {
use HasRole;
protected $fillable = array('username', 'password');
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array('password');
protected $dates = ['deleted_at'];
protected $softDelete = true;
public function editor()
{
return $this->hasOne('User_Editor', 'user_id');
}
?>
Model User_Editor:
<?php
class User_Editor extends BaseModel {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users_editors';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array();
/**
* Defiens the column names of fillable columns.
*
* #var array
*/
protected $fillable = array();
/**
* Relationships
*/
public function credentials()
{
return $this->hasMany('User_Editor_Credential', 'user_editor_id');
}
public function specialties()
{
return $this->hasMany('User_Editor_Specialty', 'user_editor_id');
}
?>
Model User_Editor_Credentials:
<?php
class User_Editor_Credential extends BaseModel {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users_editors_credentials';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array();
/**
* Defiens the column names of fillable columns.
*
* #var array
*/
protected $fillable = array();
}
Model User_Editor_Specialties:
<?php
class User_Editor_Specialty extends BaseModel {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'users_editors_specialties';
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
protected $hidden = array();
/**
* Defiens the column names of fillable columns.
*
* #var array
*/
protected $fillable = array();
}
Return
select * from User, User_Editor, User_Editor_Credentials, User_Editor_Specialty where User_Editor_Specialty.specialty In (array).
So far I've tried,
$this->data['editors'] = User::with(['editor.credentials.specialties' => function($q) use($data){
$q->whereIn('specialty',$data);
}])
->get();
But this throws an error call to undefined method specialties. Please guide, thanks.
To those who might have suffered for long trying to find a way to work around nested relationships, and also, if you are writing a join condition with whereIn (which throws call to undefined method because of a bug in the Laravel), Please find below ans,
$editors = User::with(['editor.credentials','editor.specialties']);
$this->data['editors'] = $editors->whereHas('editor', function($q) use ($a_data){
$q->whereHas('specialties',function($sq) use($a_data){
$sq->whereIn('specialty',$a_data);
});
})->get();
Update: the PR has been just merged to 4.2, so now it's possible to use dot nested notation in has methods ( ->has('relation1.relation2) ->whereHas('relation1.relation2, .. )
Your dot notated relations must be logically chained:
`with(['editor.credentials', ' editor.specialties' => function ....
You tried to search for the specialties relation on the User_Editor_Credential model.
According to the comments:
User::with(['editor.credentials','editor.specialties'])
->whereHas('editor' => function($q) use ($data){
$q->whereHas('specialties' => function($q) use ($data){
$q->whereIn('specialty',$data);
});
})->get();
or if you use my PR https://github.com/laravel/framework/pull/4954
User::with(['editor.credentials','editor.specialties'])
->whereHas('editor.specialties' => function($q) use ($data){
$q->whereIn('specialty',$data);
})->get();
It will return all the users that have related editor.specialties matching whereIn, with related editor and all its related credentials and specialties (the latter won't be filtered)