Eloquent Nested Relationship Returning null. Creating bad query - laravel-5

Good afternoon,
I have tried my very hardest to fix this problem reading through all the threads I could find on this issue but I still have had no luck in fixing my problem.
If anyone could help me I would really appreciate it!
Background
I'm using Laravel 5.2 within the Homestead environment on Ubuntu 14.04.
Database
I have 3 tables: entries, comments, authors. All PK's are named 'id'
Here is a picture of the Database Schema.
Relevent Migration Details(comment table migration):
$table->integer('entry_id')->unsigned();
$table->foreign('entry_id')->references('id')->on('entries')->onDelete('cascade');
$table->integer('author_id')->unsigned();
$table->foreign('author_id')->references('id')->on('authors');
Models:
Entry model:
public function comments() {
return $this->hasMany('App\Models\Comment');
}
Author model:
public function comments() {
return $this->hasMany('App\Models\Comment');
}
Comment model:
public function entry() {
return $this->belongsTo('App\Models\Entry');
}
public function author() {
return $this->belongsTo('App\Models\Author', 'id');
}
Controller: (Nested relationship query)
$entries = Entry::with('comments.author')->get();
At this point everything is working correctly so the above $entries contains the expected Collection made up of:
*All entires, nested within each:
**all associated comments, and nested within that
***the associated author for each comment.
The Problem
The problem occurs when I have a multiple comments in the DB which contain the same author_id value.
In this scenario, the laravel Collection gives me the expected data for the first instance(a valid nested comment collection with nested author),
but any later instances which contain the same author_id have a value of null inside the relations property which results in the following error:
1/2 ErrorException in 991fecee1bbd0eb1ec5070d2f47d9c641ca4a735.php line 24:
Trying to get property of non-object
I only get the above error when multiple author_id are the same.
Therefore I know that there is no problem in the way I am accessing the collections data in the view.
Debugging the Queries created by eloquent:
Working query: (when all comments in the DB have a unique author_id)EG:
| id | author_id | text |
|---- |----------- |------- |
| 1 | 1 | blah1 |
| | | |
select * from `entries`
select * from `comments` where `comments`.`entry_id` in ('1')
select * from `authors` where `authors`.`id` in ('1')
#relations: array:1 [▼
"author" => Author {
//all correct data displays here
Problematic query: (when there is a comment in the DB which shares an author_id with another comment)EG:
| id | author_id | text |
|---- |----------- |------- |
| 1 | 1 | blah1 |
| 2 | 1 | blah2 |
| | | |
select * from `entries`
select * from `comments` where `comments`.`entry_id` in ('1', '2')
select * from `authors` where `authors`.`id` in ('1', '2')
#relations: array:1 [▼
"author" => null
]
I believe the issue is that the 3rd query contains: "in ('1', '2')" where 2 doesn't exist.
Does anyone have any thoughts on how to fix this?
I really would like to be able to successfully utilise Laravel's built in eloquent nested relationship system.
But if this is not doable do I need to write me own custom query?
Any input would be wonderful! I'm really stuck on this.
Thanks for your time and help.

Thanks to #Ravisha Hesh.
The problem was caused by the following:
public function author() {
return $this->belongsTo('App\Models\Author', 'id');
}
The solution:
public function author() {
return $this->belongsTo('App\Models\Author', 'author_id');
}

Related

Load model one to many relation eloquent way without primary key but on multiple overlapping fields

I'm working on an older project that I've been tasked to speed up certain parts of while we work on a complete re-write since the code is just badly maintained, poorly written and outdated for what it's suppose to do.
I stumbled into an issue to the core of the project and because of this I can't change it without breaking almost everything else. So I need to load a "relation" the eloquent way (using Planning:with('availability') but there isn't a real foreign ID, it rather laps with multiple fields.
Would there be a way to load it all in one query with the overlapping fields rather than have it load separately creating an n+1 problem?
+--------------+-----------------+
| Planning | Availability |
+--------------+-----------------+
| planning_id | availability_id |
| date | date |
| startHour | startHour |
| stopHour | stopHour |
| candidate_id | candidate_id |
| section_id | section_id |
+--------------+-----------------+
From the above example you can see the overlapping fields are date, startHour, stopHour, candidate_id and section_id.
I tried get...attribute but that still loads with n+1, I tried including it with ->with(['availabilities']) but that doesn't work since I ask for the
model and not the relation:
Edit for more clarity:
Planning Model:
public function availabilities()
{
return Availability::where('section_id', $this->section_id)
->where('candidate_id', $this->candidate_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour)
->get();
}
public function availabilities2()
{
return $this->hasMany('App\Models\Availability', 'candidate_id', 'candidate_id')
}
Controller:
$plannings = Planning::with(['availabilities'])->get();
$plannings = Planning::with(['availabilities2' => function ($query) {
// $this is suppose to be Planning model but doesn't work
$query->where('section_id', $this->section_id)
->where('planningDate', $this->planningDate)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
// ---- OR ---- //
// Don't have access to planning table here
$query->where('section_id', 'planning.section_id')
->where('planningDate', 'planning.planningDate')
->where('startHour', 'planning.startHour')
->where('stopHour', 'planning.stopHour');
}])->get();
First of all to be able to load my relation I took one of the keys that matched and took the one which had the least matches which in my case was section_id.
So on my Planning model I have a function:
public function availabilities()
{
return $this->hasMany('App\Models\Availability', 'section_id', 'section_id');
}
This way I can load the data when needed with: Planning:with('availability').
However since I had a few other keys that needed to match I found a way to limit this relation by adding a subquery to it:
$planning = Planning::with([
'availabilities' => function ($query) {
$query->where('candidate_id', $this->candidate_id)
->where('startHour', $this->startHour)
->where('stopHour', $this->stopHour);
},
// Any other relations could be added here
])
->get();
It's not the best way but it is the only way I found it not getting too much extra data, while also respecting my relationship
When you want to use multiple fields in where() method you most insert a array in the where() method:
This document can help you
change your code to this:
return Availability::where([
['section_id', $this->section_id],
['candidate_id', $this->candidate_id],
['planningDate', $this->planningDate],
['startHour', $this->startHour],
['stopHour', $this->stopHour]
])->firstOrFail();

Laravel 5.7: QueryBuilder Get returning all records even custom query execution returning no records

Its really strange, i am working on my real estate related website and having two tables users and user_metas where user table have all the basic details of user and user_metas having all the meta related information of user.
user_metas table structure
----------------------------------------------------------------------
id | user_id | key | value | created_at | updated_at
----------------------------------------------------------------------
1 | 2 | age | 20 | 2019-17-01 | 2019-17-01
----------------------------------------------------------------------
2 | 3 | age | 40 | 2019-17-01 | 2019-17-01
and i just execute the query:
(new User)->newQuery()->where('user_type','rn')->with(['usermeta' => function($query) use ($minAge, $maxAge){
return $query->where('key','age')->where('value','>=',$minAge);
}])->whereHas('usermeta', function($query) use ($minAge, $maxAge){
return $query->where('key','age')->where('value','>=',$minAge);
})->toSql();
toSql() returning me:
when i am executing this query in phpmyadmin its returning nothing to me, but laravel get() returning me all records.
can anyone please tell me where i am wrong?
This is because eager loaded relations (usermeta in your case) are exacuted as a separate SQL query. They are then mapped to the User model with PHP, not at your database level.

can I use laravel 4 eloquent model or do I need to use db:query

Hi I am trying my first attempt to use ORM with Laravel. I have a big table from Drupal that I want to grab some records of and I need to join those with another table in Drupal to get the records that I care about manipulating.
Like so...
Node
----------------------------------------------------------
| Nid | type | misc other stuff | N
==========================================================
| 1 | Programs | Test Service | 1 |
----------------------------------------------------------
| 2 | Programs | Example Service | 1 |
----------------------------------------------------------
| 3 | Something else | Another Service | 1 |
----------------------------------------------------------
Fields
----------------------------------------------------------
| id | title | NID | tag |
==========================================================
| 1 | Blog Title 1 | 1 | THER |
----------------------------------------------------------
| 2 | Blog Title 2 | 2 | TES |
----------------------------------------------------------
| 3 | Blog Title 3 | 3 | ANOTHER |
----------------------------------------------------------
I want to get all the Nodes where type='Programs' and inner join those with all fields where NIDs are the same. Do I do that with an Eloquent ORM in app/model/node.php? Or a query builder statement $model=DB:table? what is the code for this? Or do I just do it in PHP?
You could do this with the ORM, but would have to override everything that makes it convenient and elegant.
Because you say you're trying to "manipulate" data in the fields table, it sounds like you're trying to update Drupal tables using something other than the Drupal field system. I would generally not recommend doing this—the Drupal field system is big, complicated, and special. There's a whole CMS to go with it.
You should move the data out of the old Drupal database and into your new database using seeds (http://laravel.com/docs/4.2/migrations#database-seeding).
Define a "drupal" database connection in your app/config/database.php, in addition to whatever you're using as a "default" connection for a new application. You can seed Eloquent models from an alternative connection in this manner:
<?php
// $nodes is an array of node table records inner joined to fields
$nodes = DB::connection('drupal')
->table('node')
->join('fields', 'node.nid', '=', 'fields.nid')
->get();
Pull the data out and put it in proper tables using Laravel migrations into normalized, ActiveRecord-style tables (http://laravel.com/docs/4.2/migrations#creating-migrations).
I prefer query builder, it's more flexible
DB::table('Node')
->join('Fields', 'Fields.NID', '=', 'Node.Nid')
->where('type', 'Programs')
->get();
Create two models in app/model (node.php and field.php) like this:
class Node extends \Eloquent {
protected $fillable = [];
protected $table = 'Node';
public function fields()
{
return $this->hasMany('Field');
}
}
class Field extends \Eloquent {
protected $fillable = [];
public function node()
{
return $this->belongsTo('Node');
}
}
Than you could do something like this:
$nodes = Node::with('fields')->where('type', 'Programs')->get();
You will get all your nodes with their relation where type is Programs.

Joining an additional table with belongsToMany()?

This question is best illustrated by an example:
users
id
name
roles
id
name
role_user
user_id
role_id
rank_id
group_id
...
ranks
id
name
groups
id
name
I can easily eager load a users table by specifying the following relationship in my User.php model:
public function roles() {
return $this->belongsToMany('Role');
}
Which will output the table below when calling User::with('roles'):
User | Role
-------------
Jon | Admin
Jan | Mod
However I have no idea how to extend this to include:
User | Role | Rank | Group
-----------------------------
Jon | Admin | Boss | Blue
Jan | Mod | Minion | Red
What I've tried doing User::with('roles', 'ranks', 'groups') but that is certainly wrong since I'm telling Laravel there are rank_user and group_user intermediate tables too but there aren't. What is the correct way?
PS: I know it's better to separate the ranks and groups into their own relationship/pivot tables, this is simply an example.
EDIT: Closest example I can find for this: https://github.com/laravel/framework/issues/2619#issuecomment-38015154
You can just treat your model's relations methods as ordinary queries and build upon them:
public function roles() {
return $this->belongsToMany('Role')
->join('role_user', 'role_user.role_id', '=', 'roles.id')
->join('ranks', 'ranks.id', '=', 'role_user.rank_id')
->join('groups', 'groups.id', '=', 'role_user.group_id');
}
Relations queries like the above are not so intuitive to understand when they get too complex, so it may be better to rethink database design, but in theory it's possible to manipulate them.

Laravel 4: one to many by on multiple columns?

I'm making a table that essentially maps rows in a table to rows in another table where the structures are as follows:
|--- Words --| |- Synonyms -|
|------------| |------------|
| id | | id |
| en | | word_id |
| ko | | synonym_id |
| created_at | | created_at |
| updated_at | | updated_at |
|------------| |------------|
Now then, I know I can essentially have the words model have many Synonyms through a function like:
public function synonyms()
{
return $this->hasMany('Synonym');
}
No problem, but this method always gets it by the the word_id, and I would like to get it from word_id OR synonym_id that way I don't have to make multiple entries in the DB.
Is there anyway I can do this?
Check laravel docs Eloquent relationships. It would only get word_id because that's the only foreign key I believe.
Also why do you have synonym_id in your Synonyms table?
I believe you are looking for polymorphic relationship.
http://laravel.com/docs/eloquent#polymorphic-relations
I think your best bet is to create a many-to-many relationship with words on itself using the synonyms table as your pivot table.
Add this to your Word model.
public function synonyms()
{
return $this->belongsToMany('Word', 'synonyms', 'user_id', 'synonym_id');
}
Using it:
$word = Word::where('en', '=', 'someword')->first();
foreach($word->synonyms as $synonym) {
// This method would probably return the same word as a synonym of itself so we can skip that iteration.
if($synonym->en == $word->en) {
continue;
}
// Echo the synonym.
echo $synonym->en;
}
I'm a bit confused on you wanting to be able to find synonyms by the word_id or synonym_id but I think if you are using the many-to-many, it won't matter because if you know the synonym, it's still technically just a word, and you'd do the exact same thing.

Resources