hasOne with null-able in laravel not working - mysql

I have a customer table which has a field called 'policy_id', where policy_id points to policy table. It is a null-able field, ie. Some customers may not have a policy.
I have a relationship code like this in Customer.php
public function policy() {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
}
But when I issue a search request I am getting error like this:
Illuminate\Database\Eloquent\ModelNotFoundException: No query results for model [App\Models\Policy]
If I modify the function like this:
public function policy() {
if ($this->getAttribute('policy_id')) {
return $this->hasOne('App\Models\Policy', "id", "policy_id");
} else {
return null
}
}
But I am getting an error like this:
Call to a member function getRelationExistenceQuery() on null
Here is my search code:
$c = new Customer();
return Customer::doesntHave('policy')->orWhere(function (Builder $query) use ($req) {
$query->orWhereHas('policy', function (Builder $query) use ($req) {
$p = new Policy();
$query->where($req->only($p->getFillable()))
->orWhereBetween("policy_period_from", [$req->policy_period_start_from, $req->policy_period_start_to])
->orWhereBetween("policy_period_to", [$req->policy_period_end_from, $req->policy_period_end_to])
->orWhereBetween("payment_date", [$req->payment_date_from, $req->payment_date_to]);
});
})->where($req->only($c->getFillable()))->get();
Am I missing something or are there any other ways to do this?
PS: While debugging the above search code is returning successfully, but the exception happening from somewhere inside Laravel after the prepareResponse call.
Thanks in advance.

return $this->hasOne('App\ModelName', 'foreign_key', 'local_key');
Change the order, put the foreign_key policy_id in front of id
In your Customer Model, you need to use belongsTo method:
public function policy() {
return $this->belongsTo('App\Models\Policy', "policy_id", "id");
}
And In your Policy Model, use hasOne:
public function customer() {
return $this->hasOne('App\Models\Customer', "policy_id", "id");
}

First of all, you placed the wrong params.
$this->belongsTo('App\Models\Policy', "FK", "PK");
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id');
}
And for null value of policy_id you can use withDefault();
public function policy() {
return $this->belongsTo('App\Models\Policy','policy_id', 'id')->withDefault([
'name' => 'test'
]);;
}

there's a number of problems there but can you perhaps specify the namespace and the class of both your models - Customer and Policy.
By default, the models you create with php artisan make:model will use the \App namespace e.g. \App\Customer and \App\Policy.
Just double check that.
Also, with regards to the relationship, if the Laravel conventions have been followed you could just:
In the Customer model
public function policy() {
return $this->belongsTo(Policy::class);
}
In the Policy model
public function customer() {
return $this->hasOne(Customer::class);
}
of if a multiple customers can be under one policy
public function customers() {
return $this->hasMany(Customer::class);
}
Good luck

Related

laravel morphToMany of parent model how to use in subquery?

Current laravel models relations: ParentModel can have many documents, ChildModel can have many documents, same Documents can be belonged to any of ParentModel and ChildModel.
Also ChildModel always belongs to one ParentModel. ParentModel can have multiple ChildModels.
App\ParentModel relationships
...
public function childmodels()
{
return $this->hasMany('App\ChildModel');
}
public function documents()
{
return $this->morphToMany('App\Document', 'documentable');
}
...
App\ChildModel relationships
...
public function parentmodel()
{
return $this->belongsTo('App\ParentModel');
}
public function documents()
{
return $this->morphToMany('App\Document', 'documentable');
}
...
App\Document
...
public function parentmodels()
{
return $this->morphedByMany('App\ParentModel','documentable');
}
public function childmodels()
{
return $this->morphedByMany('App\ChildModel','documentable');
}
...
Now I'm trying to get all records of ChildModels (1) which have documents with specific type and (2) its ParentModel can also have documents.
The (1) first goal can be reached with such ChildModel method.
/* checking GET param to join this condition to final query */
if($request->has('report') && $request->input('report') == 'on') {
/* get all documents related to CurrentModel */
$query->whereHas('documents',function (Builder $query) {
$query->where('type', 1);
});
}
But this obviously doesnt include records of ChildModels, ParentModel of which has documents with specific type.
So the question is: how to include such condition into ChildModel query builder?
Found the easy way to access relations in builder
$query->whereHas('parentmodels.documents', function(Builder $query){
$query->where('type',1);
});

many-to-many relationship: order by on pivot table not working

I have these relationship between school and associate models:
// School model
public function associates()
{
return $this->belongsToMany('Associate', 'school_associate', 'school_id', 'associate_id')
->withPivot('start_date', 'end_date');
}
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date');
}
I need to get all associates of one school ordered by start_date.
This is what I tried without success (in this try I am searching in all schools):
dd(\App\Associate::with(['schools' => function ($q) {
$q->orderBy('pivot_start_date', 'desc');
}])->toSql());
And I get this sql (notice no order by clause):
select * from `associate`
I tried to edit the relationship like this:
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date')
->orderBy('pivot_start_date', 'desc'); // also tried without "pivot_"
}
And according to this post, I also tried :
// Associate model
public function schools()
{
return $this->belongsToMany('School', 'school_associate', 'associate_id', 'school_id')
->withPivot('start_date', 'end_date')
->orderBy('school_associate.start_date', 'desc');
}
But I always get the same query and the results are not ordered.
I solved using query builder in this way.
This function is in Associate model:
public function scopeLast($query, $school_ids = [])
{
$query->join('school_associate', "{$this->table}.{$this->primaryKey}", '=', 'school_associate.associate_id')
->join('school', 'school.school_id', '=', 'school_associate.school_id')
->whereIn('school.school_id', $school_ids)
->orderBy('school_associate.start_date', 'desc');
return $query;
}

Yii2: Model column alias

I have some silly column names in my database, is there an easy way to alias a column name so it's always used for example:
public function columnAlias(){
return ['id'=>'ID', 'foo'=>'Bar'];
}
$model->id === $model->ID
$model->foo === $model->Bar
If it's only for accessing the attributes on the model you can write a get function.
So adding:
public function getId() {
return $this->ID;
}
public function getFoo() {
return $this->Bar;
}
will enable you to access ID and Bar like
$model->id;
$model->foo;
This works because of Yii2's use of the magic method __get: https://github.com/yiisoft/yii2/blob/master/framework/base/Component.php#L134

Adding withCount to collection json

So i've got two models, Client and Project, and Client has a hasMany relationship with projects. I'm just trying to add the project count for the client into the JSON response but I can't get it to work.
My controller just returns all the projects;
public function index(Client $client)
{
return $client->all();
}
And my model contains the below;
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function getClientProjectsCountAttribute()
{
return $this->withCount('clientProjects');
}
This just adds client_projects_count to the response but it's an empty array. I'm close, if I dd($this->withCount('clientProjects')->get()) I can see the client_projects_count with the correct count, but if I remove the dd and keep the ->get() then I get an internal server error.
Also, it is possible to only load this for the index() method rather than every one?
From the Documentation
$clients = Client::withCount('projects')->get();
foreach ($clients as $client) {
echo $client->projects_count;
}
So I managed to resolve it myself, although I'm sure their must be a nicer way.
Client.php
protected $appends = ['client_projects_count'];
/**
* Create a relationship between this client and it's projects
*/
public function clientProjects() {
return $this->hasMany('App\Project');
}
public function clientProjectsCount()
{
return $this->clientProjects()->selectRaw('client_id, count(*) as aggregate')->groupBy('client_id')->get();
}
public function getClientProjectsCountAttribute()
{
return isset($this->clientProjectsCount()[0]) ? $this->clientProjectsCount()[0]->aggregate : 0;
}

How to eager load relations for single eloquent object?

I have this Comment model with belongs to relationship.
class Comment extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
While in my controller:
public function store(Request $request)
{
$comment = $this->dispatch(
new StoreCommentCommand($request->body, Auth::user()->id, $request->post_id)
);
return $comment;
}
When I return the $comment I also want nested user object, how can I do that?
You need to use the with() on Builder object, whereas currently you are using on eloquent object. This will be your working code.
return Comment::with('user')->where('id', $comment->id)->first();
Just eager load it...
return $comment->with('user');
In case anyone stumbles upon this, you can just assign the user to the comment before returning it.
$comment->user = Auth::user()
return $comment
The with eager-loading function isn't needed here.