So, i'm trying to learn laravel and i kind of got stuck on one to many relationship. So, i have a page with 3 links on it, and logging into the db the click on those links. All good, i click on link, it registers in the db the id of the link but when i try to print them in the view i get "Trying to get property of non-object".
My models:
class NiceAction extends Model
{
public function logs(){
return $this->hasMany('App\NiceActionLog');
}
}
class NiceActionLog extends Model
{
public function log(){
return $this->belongsTo('App\NiceAction');
}
}
My controller:
This is how i log into the db on click:
public function getNiceAction($action, $name = null){
if ($name == null) {
$name = 'you';
}
$nice_action = NiceAction::where('name', $action)->first();
$nice_action_log = new NiceActionLog();
$nice_action->logs()->save($nice_action_log);
return view('actions.nice', ['action' => $action, 'name' => $name]);
}
And this is how i pass the loggs to the view:
public function getHome(){
$actions = NiceAction::all();
$logged_actions = NiceActionLog::all();
return view('home', ['actions' => $actions, 'logged_actions' => $logged_actions]);
}
And this is in my view:
#foreach($logged_actions as $loggedAction)
<li>{{ $loggedAction->log->name}}</li>
#endforeach
LATER EDIT:
My tabels looks like this if this could be the problem:
nice_action_logs table:
public function up()
{
Schema::create('nice_action_logs', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('nice_action_id');
});
}
nice_actions table:
public function up()
{
Schema::create('nice_actions', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('name');
$table->integer('niceness');
});
}
Your getHome() method has the following line:
$logged_actions = NiceAction::all();
I'm assuming this is actually meant to be:
$logged_actions = NiceActionLog::all();
Since you're still getting the error after correcting this, you probably have a NiceActionLog record that does not have an associated NiceAction record. Because of this, $loggedAction->log will be null, and then $loggedAction->log->name will give you the error you're seeing.
You can check to see if log exists before attempting to access a property on it:
#foreach($logged_actions as $loggedAction)
<li>{{ $loggedAction->log ? $loggedAction->log->name : 'No Log' }}</li>
#endforeach
Based on your update with the table definitions, your belongsTo relationship is not defined correctly. The belongsTo side of the relationship builds the foreign key name based on the name of the relationship method. You've named your relationship method log(), so it will look for a field on the nice_actions_logs table named log_id. You either need to rename your relationship method to match the foreign key field, or you need to pass the foreign key field to use in as the second parameter.
class NiceActionLog extends Model
{
// change the relationship method so the foreign key name will be correct
public function nice_action() {
return $this->belongsTo('App\NiceAction');
}
}
Or
class NiceActionLog extends Model
{
// leave relationship method alone, but supply the foreign key to the relationship
public function log() {
return $this->belongsTo('App\NiceAction', 'nice_action_id');
}
}
The hasMany side of the relationship builds the foreign key name based on the class name, so since your class is named NiceAction, it will look for the nice_action_id field, which is correct. There is no need to change this relationship definition or name.
Try this code to view what $loggedAction really contains:
#foreach($logged_actions as $loggedAction)
<li>{{ var_dump($loggedAction)}}</li>
#endforeach
Then, look at the output, and see what's wrong.
Possible answers:
$loggedAction->log might be null
$loggedAction might be an array (not so possible)
You can check the logs variable before putting like this:
#foreach($logged_actions as $loggedAction)
<li>
#if (property_exists($loggedAction->log,"name"))
{{$loggedAction->log->name}}
#else
No Log
#endif
</li>
#endforeach
Ok i've got it. I've changed my models into these and now works:
class NiceAction extends Model
{
public function NiceAction(){ //changed function's name
return $this->hasMany('App\NiceActionLog');
}
}
class NiceActionLog extends Model
{
public function NiceActionLog(){ // changed function's name
return $this->belongsTo('App\NiceAction', 'nice_action_id'); // added nice_action_id parameter
}
}
Related
I am working on some kind of ambulance app and I need help on how to load relationship.
So, I have table appointment_statuses (and it is populated over the seeder because I need only 3 states - Done, In Progress, Not Performed), I have also the many-to-many relationship between the User model and Appointment model (appointment_user table which holds only IDs of both models) and now I am working on EMR system which means I can check all appointments that patient had in history.
Here is the image of the issue
So under "Status" I want to load name of that ID from appointment_statuses table instead to have only ID.
These tables have this structure:
Appointments
Status
These tables have these values:
Appointments table
Appointment statuses table
These are relations:
User:
public function role()
{
return $this->belongsTo(Role::class);
}
public function patient()
{
return $this->hasOne(Patient::class);
}
public function appointments()
{
return $this->belongsToMany(Appointment::class);
}
Appointment:
public function users()
{
return $this->belongsToMany(User::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function appointmentStatus()
{
return $this->belongsTo(AppointmentStatus::class);
}
Appointment_Statuses:
public function patient()
{
return $this->belongsTo(Patient::class);
}
public function appointment()
{
return $this->belongsTo(Appointment::class);
}
Here is a controller which is responsible for emr:
After I have added to controller this:
$user = User::with(['appointments', 'appointments.appointmentStatus'])->where('id', $id)->firstOrFail();
I get this in frontend:
{{ dd($user->toArray()) }}
SOLUTION TO THIS ISSUE
For anyone in future who gets this kind of issue just check the convention about the naming of the foreign keys. In my example, it was the issue, and if you are not sure that your foreign key name is correct then just in the model provide more information like this:
public function appointmentStatus()
{
return $this->belongsTo(AppointmentStatus::class,'appointment_statuses_id','id');
}
You can use nested relationship
$user=User::with(['appointments','appointments.appointmentStatus'])
->where('id',$id)
->first();
Also you have to modify relationship
public function appointmentStatus()
{
return $this->belongsTo(AppointmentStatus::class,'appointment_statuses_id','id');
}
For anyone in future who gets this kind of issue just check the convention about the naming of the foreign keys. In my example, it was the issue, and if you are not sure that your foreign key name is correct then just in the model provide more information like this:
public function appointmentStatus()
{
return $this->belongsTo(AppointmentStatus::class,'appointment_statuses_id','id');
}
I am working on a project in which there are events, which each relate to two single forms on two separate relations – booking and survey. These forms are identically constructed, making it seem unnecessary to use two entirely distinct form models – I instead wanted to use a polymorphic relation, but it appears that isn't possible.
What is the appropriate way to structure this relationship?
Events have one or no booking form
Events have one or no survey form
Forms are a separate, single table
What I have tried:
Polymorphic relationship: Not compatible with two relations to the same model.
Has one relationship: This used a booking_id and survey_id but refused to set either of these fields.
Has many relationship with a type field: Made it difficult to easily save the forms, as it wasn't possible to save to the single relationship. There was also no restriction on the number of forms.
class Event extends Model
{
public function booking()
{
return $this->hasOne(Form::class, 'id', 'booking_form_id');
}
public function survey()
{
return $this->hasOne(Form::class, 'id', 'survey_form_id');
}
}
...
class Form extends Model
{
public function event()
{
return $this->belongsTo(Event::class);
}
}
...
$event = new Event;
$event->name = 'Event';
$event->save();
$booking = new Form;
$booking->name = 'booking';
$event->booking()->save($booking);
$survey = new Form;
$survey->name = 'survey';
$event->survey()->save($survey);
...
Schema::create('events', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->unsignedInteger('booking_form_id')->nullable()->index();
$table->unsignedInteger('survey_form_id')->nullable()->index();
$table->timestamps();
});
Schema::create('forms', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->timestamps();
});
What would be preferable:
Using a polymorphic relationship which would allow forms to be used in other parts of the application.
Using multiple hasOne relationships to limit the number of forms to one for each type.
I think you got your param order wrong. It's hasOne($related, $foreignKey, $localKey)
class Event extends Model
{
/* if you haven't changed the default primary keys, $localKey should be equal to 'id' */
public function booking()
{
return $this->belongsTo(Form::class, 'booking_form_id');
}
public function survey()
{
return $this->belongsTo(Form::class, 'survey_form_id');
}
}
class Form extends Model
{
public function booking_event()
{
return $this->hasOne(Event::class, 'booking_form_id');
}
public function survey_event()
{
return $this->hasOne(Event::class, 'survey_form_id');
}
}
Now there's 2 ways you can go about this.
If a Form can belong to both kind of events, you need to return a collection when accessing $form->event.
If a Form can belong to only one kind of event, you need to guess which kind and return the model when accessing $form->event.
# Form model
# 1. can be achieved using an accessor. Cannot be eager loaded but can be appended with the $appends Model property
public function getEventsAttribute()
{
return collect([$this->booking_event, $this->survey_event]);
}
# Form model
# 2. can be achieved using a relationship that guesses which relation it should return. Since it returns a relationship, it can be eager loaded.
public function event()
{
return ($this->booking_event()->count() != 0) ? $this->booking_event() : $this->survey_event();
}
I got few relations in my model Reply:
/** Reply.php */
public function thread()
{
return $this->belongsTo(Thread::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
My reply table got user_id and thread_id
And then I try to get user name I do it like this:
$reply->user->name
And it works.
But when I try to get thread title:
$reply->thread->title
I got error:
Trying to get property title of non-object
And if need to get title only method I know is:
$reply->thread['title']
What is the difference between methods I use? Why in one case I get user as an object but in another I get thread as an array?
Update:
My Reply model relations:
public function user()
{
return $this->belongsTo(User::class);
}
public function favorites()
{
return $this->morphMany(Favorite::class, 'favorited');
}
public function thread()
{
return $this->belongsTo(Thread::class);
}
My Thread model relations:
public function replies()
{
return $this->hasMany(Reply::class);
}
public function user()
{
return $this->belongsTo(User::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
And my User model relations:
public function threads()
{
return $this->hasMany(Thread::class)->latest();
}
public function replies()
{
return $this->hasMany(Reply::class)->latest();
}
When you make
one to one
relation then relation will return object
When you make
one to many
relation will return array of objects
The Model class implements the Arrayable interface, this means that you can access the attributes also as if it were an array, this is why this work:
$reply->thread['title'];
When you use a BelongsTo relationship, this expect to return an object (an instance of your model) or null in case this relation isn't set, this is why you can use "magic methods" to access attributes like this:
$reply->thread // an object
$reply->thread->title // getting attributes using magic methods
But, what happen when the relationship isn't set? well, the relationship will return null so when you do this:
$reply->thread->title
It will throw an arror:
Trying to get property title of non-object
Because you are trying to access the title attribute of null.
Update:
This is where -I think- the error is. With the newest version of Laravel (as of today: Laravel 5.8), the primmary keys types has changed from integers to bigIntegers(), and this is for all the tables:
Schema::create('replies', function (Blueprint $table)
{
$table->bigIncrements('id'); // <---- equivalent to bigInteger
$table->integer('user_id')->unsigned;
$table->integer('thread_id')->unsigned;
$table->text('body');
$table->timestamps();
});
So, your foreign keys should be also big integers, try this:
Schema::create('replies', function (Blueprint $table)
{
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id'); // <-----------
$table->unsignedBigInteger('thread_id'); // <-----------
$table->text('body');
$table->timestamps();
});
Check this article related this issue.
I think that the only reason for this error would be a null return from the relation. In that case
$reply->thread['title']
won't work, can you check it please ?
If the $reply->thread['title'] works, I would like to see the output of dd($reply->thread); please.
If it does not work and the cause of the error is indeed a null return, you just have to check that
$reply->thread is not null before using it.
Let me know if it helped you :)
I'm trying to define a one-to-many relationship in laravel where I have a Movimentacao class and a TipoMovimentacaoFin. One Movimentacao is one type TipoMovimentacaoFin and a TipoMovimentacaoFin can have many Movimentacao(s) associated with it. For it, in their models I declared:
class Movimentacao extends Model
{
public $timestamps = false;
public function tipo(){
return $this->belongsTo('Patropi\TipoMovimentacaoFin', 'tipo', 'tipo');
}
}
and
class TipoMovimentacaoFin extends Model
{
public $timestamps = false;
public function movimentacaos(){
return $this->hasMany('App\Movimentacao', 'id', 'tipo');
}
}
At the database level I got:
tipo_movimentacao_fins.tipo (varchar) as PK plus other columns and movimentacaos.tipo as FK to the first table. The names of the tables were auto generated by Laravel standards. The migrations for the tables are:
Schema::create('tipo_movimentacao_fins', function (Blueprint $table) {
$table->string('tipo', 50);
$table->char('relacionado_empresa', 1);
$table->char('entrada_saida', 1);
$table->primary('tipo');
});
Schema::create('movimentacaos', function (Blueprint $table) {
$table->increments('id');
$table->float('montante', 12, 2);
$table->dateTime('data_prevista');
$table->dateTime('data_vencimento')->nullable();
$table->char('esta_pago', 1);
$table->float('juros', 6, 2)->nullable();
$table->smallInteger('periodo_juros')->nullable();
$table->string('tipo', 50);
$table->string('referente_a', 255);
$table->string('obs', 255)->nullable();
$table->foreign('tipo')->references('tipo')->on('tipo_movimentacao_fins');
});
Now the problems begins in the View when I'm trying to access the type (tipo) of the Movimentacao. I give to the view the collections with $movimentacaos = Movimentacao::with('tipo')->orderBy('data_prevista', 'desc')->get() and when I loop #foreach($movimentacaos as $mov) if I access $mov->tipo I get the value of movimentacaos.tipo column and that's as expected. If I try to access some column of the tipo_movimentacao_fins for a given $mov I can't:
{{ $mov->tipo()->relacionado_empresa }}
Gives Undefined property error. So, since the documentation of laravel is kinda messy, I'm trying to analyse what's the return of the ->tipo() call and more errors occurs:
If I just write {{ $mov->tipo() }} it gives me the error "htmlspecialchars() expects parameter 1 to be string, object given".
If {!! $mov->tipo() !!} gives "Object of class Illuminate\Database\Eloquent\Relations\BelongsTo could not be converted to string".
If {{ var_export($mov->tipo()) }} gives "var_export does not handle circular references".
Finally, if I try a var_dump it gives Allowed memory size of 134217728 bytes exhausted (tried to allocate 98582528 bytes)
Also the Laravel docs didn't helped too much in regards of how to access the BelongsTo Object instance.;
EDIT I edited the code as recommended in the answers and comments but the same problems persists.
Your relationship is bad. Should be:
class Movimentacao extends Model
{
public $timestamps = false;
public function tipo(){
return $this->belongsTo('App\TipoMovimentacaoFin', 'tipo');
}
}
and:
class TipoMovimentacaoFin extends Model
{
public $timestamps = false;
public function movimentacaos(){
return $this->hasMany('App\Movimentacao', 'tipo');
}
}
Please take a look at the DOCs
It should be belongsTo relationship with correct FK definition:
public function tipo()
{
return $this->belongsTo('App\TipoMovimentacaoFin', 'tipo');
}
And another relationship should look like:
public function movimentacaos()
{
return $this->hasMany('App\Movimentacao', 'tipo');
}
This will work if tipo is in the movimentacaos table and points to id in the tipomovimentacaofins table.
I'm trying to get iot to show The items within an order and i keep getting this error
These are my models
class westcoorder extends Model
{
protected $table = 'westcoorders';
protected $with = 'westcoorderitem';
protected $fillable = ['is_sent', 'is_delivered'];
/**
* #return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function westcoorderitem()
{
return $this->hasMany('App\westcoorderitem');
}
}
class westcoorderitem extends Model
{
protected $table = 'westcoorderitems';
protected $fillable = ['westcoorder_id','quantity', 'productName', 'productCode', 'price'];
public function westcoorder()
{
return $this->belongsTo('App\westcoorder');
}
}
This is my controller
public function onGoingOrder($orderNumber)
{
$orderNumber = westcoorder::where('id', $orderNumber)->firstOrFail();
$items = westcoorderitem::where('westcoorder_id', $orderNumber)->get();
return view('westco.onGoingOrder', compact('orderNumber', 'items'));
}
And this is what i have in my view
<div class="panel-heading">Order #if ($orderNumber) {{ $orderNumber->id }} #endif Items</div>
<div class="panel-body">
#if($items)
{{ $items->productName }}
#endif
</div>
Here is what my tables looks like
Schema::create('westcoorders', function (Blueprint $table)
{
$table->increments('id');
$table->tinyInteger('is_sent')->default(0);
$table->tinyInteger('is_delivered')->default(0);
$table->timestamps();
} );
Schema::create('westcoorderitems', function (Blueprint $table)
{
$table->increments('id');
$table->Integer('westcoorder_id'); // fk for westcoOrder.id
$table->string('quantity');
$table->string('productName');
$table->string('productCode');
$table->decimal('price');
$table->timestamps();
} );
And this is the error that I'm getting
Undefined property: Illuminate\Database\Eloquent\Collection::$productName
Like your error states:
Undefined property: Illuminate\Database\Eloquent\Collection::$productName
You are trying to access a property on a Collection, instead of a Model.
First, you can make use of the relationship you created, like so:
$order = App\westcoorder::where('id', $orderNumber)->with('westcoorderitem')->firstOrFail();
This will ensure the order items will be included with the result, instead of executing another query to fetch them.
You can then pass on the $order to the view:
return view('welcome', compact('orderNumber', 'order'));
(You can probably just leave out the orderNumber which was the actual order, as well)
Then you can access the order in your view and loop through the items like this:
#foreach($order->westcoorderitem as $item)
{{ $item->productName }}
#endforeach
FK
Another tip could be to update your table to use indexes to improve performance and make it neat, like the FK you mention in the comment of your create migration. You can make a migration to update it, like:
$table->foreign('westcoorder_id')->references('id')->on('westcoorders');
And/or expand on this, according to your needs (cascading, etc).