Strange Eloquent belongsTo() return null. Foreign key property being displayed but not accessible - laravel

Environment : PHP 8.1.4, Laravel 9
Eloquent Model : Market Model, Sequence Model
Sequence Model belongs to Market Model.
// Sequence.php
public function market()
{
return $this->belongsTo(Market::class);
}
Market and Sequence table has each data properly..
Problem is, Sequence model cannot access its market properly. It looks like $this->market() works fine but $this->market returns null.
// Some method in Sequence.php
private function loadCollection(): CandleCollection
{
dd($this->market(), $this->market);
return $this->collection = $this->market->createCandleCollection($this->label, $this->size);
}
When I dump $this->market(), it returns BelongsTo object and $this->market, it returns null.
Also, $this->market()->toSql() returns
select * from `markets` where `markets`.`id` is null
Strange thing is, it works actually fine just yesterday and I didn't change its code....;; I believe unknown side effect can cause this but I never experience those kine of issue. Any insight help...

Sorry.. I just found that there is an empty method getAttribute($key).. and coworker said he made some joke.

Related

Call to a member function first() on null

When I try to fetch my user data, I receive the error
Call to a member function first() on null
public function show($id) {
$user=User::findOrFail($id);
$employee = $user->employees->first();
return view('admin.profile')
->with(['employee' => $employee , 'user' => $user]);
}
The problem is probably in your User model.
Check that you have declared the employees relationship:
public function employees()
{
return $this->hasMany(Employee::class); // I'm assuming you have a Employee model with expected column names, but feel free to replace everything with what you actually have in your app
}
If the problem persists, edit your question with your tables structure and your models.
It's very useful to understand the difference between $user->employees and $user->employees().
$user->employees: returns a Collection of employee models, or null if none are found.
$user->employees(): returns a query builder instance that you can chain additional conditions to (where's, etc).
Both options have a first() option available to them, but one is using a Collection method, where the other is using the query builder method.
Some have already suggested this, and I will as well - the safer and simplest solution to your problem is to use the query builder version of the relationship, since there is no risk of the employees() result being null. It also has the added benefit of not needing to load the entire relationship into a collection just to get the first result.
In short: $user->employees()->first(); is the best way to go.

Laravel - why is a Model's relations not being loaded?

I have a Laravel 5.3 site and I think maybe I have some weird things going on due to some actions happening in API controllers and some happening in the regular controllers.
Or maybe an issue where at some times I am dealing with a Model, and sometimes with an Eloquent Collection.
The issue is, I am trying to retrieve relations on a Model and am getting null.
For instance, I have course Model that relates to week Model.
In course Model I get week items as
public function weeks()
{
return $this->hasMany(Week::class, 'course_id');
}
In backend, these relations get sent in this way:
$course->load('weeks')
All is good.
But when course item gets deleted and I try and take action in the week controller as
static::deleting(function($course) {
$course->weeks->delete();
});
$course->weeks is null. At that time I see in the database that all is good and this course items does indeed have a week item related, but $course shows 0 relations.
So something odd is happening where $course->webinars is not grabbing the week items related.
Is there something that I am fundamentally doing wrong? Maybe it is because in the models I have these sorts of statements:
protected $table = 'Week';
Are these preventing the relations from being pulled? I always thought that is I had some function in a model that returns relations that those relations would always be available when I use syntax $course->weeks.
Ideas?
Thanks again,
You can simply setup migrations to automatically delete from weeks if you delete a course, provided you have foreign key relationship.
If you have a column course_id in weeks table then add this into your migration
$table->foreign('course_id')
->references('id')->on('courses')
->onDelete('cascade')
I think you can use Observers. In your AppServiceProvider, first register the observer.
public function boot()
{
Course::observe(CourseObserver::class);
}
Now, add an Observer class.
class CourseObserver
{
public function deleting(Course $course)
{
$course->weeks()->delete();
}
}

Laravel - latest record from relationship in whereHas()

This is my first post in here, so please forgive any mistakes :)
I'm currently working on the project of stock management application (Laravel). I came to the point where anything I do doesn't work, so now I beg for help with it.
I have a table with products, of which some are in the relationship with the others. Everything happens in one table. If the product has a child, the child overrides the parent.
products table view
Then, all the queries I run on them use the following logic:
If the item doesn't have any child, use it.
If the item has children, use the latest child (highest id)
Now I have the relationships created in model file:
public function childItems(){
return $this->hasMany('\App\OrderItem','parent_id');
}
public function parentItem(){
return $this->belongsTo('\App\OrderItem','parent_id');
}
public function latestChild(){
return $this->hasOne('\App\OrderItem','parent_id')->orderBy('id','desc')->limit(1);
}
The problem with latestChild() relationship is, that when you run this query:
\App\OrderItem::find(7)->latestChild()->get()
It works fine and returns only one (latest)(id 6) record in relationship - to do it I had to add orderBy and limit to hasOne().
But when I want to use this relationship in scopes, so in whereHas method, it doesn't work properly, as takes any of the children instead of the latest one.
public function scopeDue($query){
return $query->where(function($q){
$q->has('childItems','==',0)->has('parentItem','==',0)->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
})->orWhere(function($q2){
$q2->has('childItems')->has('parentItem','==',0)->whereHas('childItems',function($q3) use($q2){
$q3->whereDate('due_date','=', Carbon::today()->toDateString())->whereNull('return_date');
});
})->with('latestChild');
}
However, with() at the end returns the right record.
I think, the reason it works so is because my relationship latestChild() returns all the children (despite hasOne()) and when i use it in whereHas it ignores the filtering functions I applied.
I know it's a little bit complex from what I describe, but to explain it better I will use an example. Executing the following in tinker
\App\OrderItem::due()->get();
Should return only record id 2, as the number seven has children, where of course id 5 is due, but the latest child is id 6 which is not due.
I hope I've explained it enough to let you help me, as I'm already going crazy with it.
If you have any ideas on how I could achieve what I need by changing exisiting one or changing the whole logic of it, please help!
Thanks,
Darek
Try this one:
->with(
[
'latestChild' => function (HasOne $query) {
return $query->latest('id')->limit(1);
}
]
);
I think the problem is in your latestChild() method where you do a limit(1). Why don't you try the last() method instead?
So:
public function latestChild(){
return $this->hasOne('\App\OrderItem','parent_id')->last();
}
EDIT:
What about returning the value like this:
public function latestChild(){
$item = App\OrderItem::all()->last();
return $item;
}

Attaching new relation and returning the model

I have a User that has many Positions. I want to update the User (the model and the relation Position), and then return the updated result.
The input will be an array of the format
{
first_name,
last_name,
email,
... (other user information),
positions : [
{
id,
name,
descriton
},
...
]
}
My update function currently is
public function update($id)
{
// This is a validation that works fine
if ( ! User::isValid(Input::all())) return $this->withValidation(User::$errors);
$user = User::with('positions')->find($id);
$new_ids = array_pluck(Input::get('positions'), 'id');
$user->positions()->sync($new_ids);
$user->update(Input::all());
return $user;
}
My User and its permissions are updated, but I still get the old $user's relationship back (i.e. the basic information is updated, and the new positions are updated in the DB, but the returned result is the NEW basic information with the OLD positions).
Right now to fix this, I recall the User::with('positions')->find($id) at the end and return that. But why isn't my code above working?
Correct, sync doesn't update related collection on the parent model.
However you should use $user->load('positions') to reload the relation, it will call only 1 query, without fetching the user again.
Also, you can call load on the Collection:
$user->positions->load('anotherRelation');
because here positions is a Collection, which have load method to lazy load all the related models for each item in the collection.
This would not work, since positions() returns relation object, not collection:
$user->positions()->load('anotherRelation');

getting model belongsTo attributes

class Batch extends Eloquent {
public function coupons() {
return $this->hasMany('Coupon');
}
}
class Coupon extends Eloquent {
public function batch() {
return $this->belongsTo('Batch');
}
public function price() {
$batch = $this->batch;
return $batch->price;
}
}
$coupon->price gives me this error:-
LogicException Relationship method must return an object of type
Illuminate\Database\Eloquent\Relations\Relation
However, $coupon->batch->price works just fine.
What am I missing?
Your issue here is that you define a non-relationship method price() but you call it as if it was a relationship method (i.e. you call it as a property and not as a method).
The code you should be using to get the Coupon's price is:
$coupon->price();
The reason the relationship thing works (i.e. $coupon->batch over $coupon->batch()) is that Laravel has some special logic in - basically it catches you trying to access a property (->batch in this case) and checked to see if there's a corresponding method (->batch()). If there is one, it calls it and expects it to return a relationship, and then it calls ->first() of ->get() on that relationship depending on whether it's a single-result or a multiple-result relationship.
So what's happening in your code here is that you call $coupon->price and Laravel, behind the scenes, decides that being as there's a ->price() method it must be a relationship. It calls the method, checks that it returns one of the Laravel relationship types, and when it doesn't, throws that LogicException.
The general rules of thumb is this:
If you want an actual property (i.e. anything defined on the table) or the results of a relationship query, use property access
If you want anything else (i.e. behaviour you're defined using a method) you must call the method directly
Also, sometimes there is a good reason to call a relationship as the method rather than the property - calling the method returns something you can add query builder constraints on to, whereas calling the property gets you all the results. So say Coupons can be enabled or disabled (for example), the following holds:
$batch->coupons gets you all coupons that the batch has
$batch->coupons()->whereEnabled(1)->get() gets you all enabled coupons for a given batch
$batch->coupons()->orderBy('order')->get() gets you all the coupons that the batch has, ordered by a field called order
$coupon->batch gets you the given coupon's batch
Hopefully that explains the difference between Eloquent's use of methods versus properties for relationships, and why all augmented behaviour (like price on coupon in your example, but not price on batch, which is inherent behaviour) must be called as a method.
Take a moment to realize what objects you actually have here.
When you call $this->batch; you're no longer chaining the relationship queries - you've actually retrieved the information from the database already. In order to define that query you'd have to do it one of several ways including:
Coupon::with('batch.price')->get();
You could of course do it with relationships but it's late and I'm not sure where exactly Price belongs in the scheme of this since I don't see a method for it associated with batch. Presumably you could do:
public function price()
{
return $this->batch->price;
}
if Price is a derivative of Batch.

Resources