A Laravel relation is displaying null on eager load. However, it works when the relation is accessed normally.
class Student extends Model
{
use SoftDeletes;
public $incrementing = false;
protected $primaryKey = 'id'; // or null
protected $guarded = [];
public function document()
{
return $this->hasOne('App\Model\Document');
}
public function contact()
{
return $this->hasOne('App\Model\Contact');
}
}
The contract relation returns null when I use the following.
Student::with('contact')->get()
However it works when I do the following. What could be the cause of this?
$student = Student::findOrFail($id);
$contact = $student->contact;
Student::with('contact')->get(); returns a Collection of Student instances, which you can loop and access the contact relationship:
$students = Student::with('contact')->get();
foreach($students AS $student){
dd($student->contact);
// Can be `null` or an instance of `Contact`
}
When you call Student::findOrFail($id);, you're getting a single instance of Student, which you can directly access contact:
$student = Student::with('contact')->findOrFail($id);
dd($student->contact);
// Again, can be `null` or an instance of `Contact`
The with() clause is eager loading, and doesn't do anything until you try to access $student->contact, but due to the nature of hasOne(), it can be null.
Related
I am developing an application using Laravel and Eloquent ORM, it uses a database filled with event information.
I have successfully implemented attach() in the relevant controllers for both my user and role models.
My Event model can have many Links. A Link can have Many events.
My problem is that the attach() is not supplying the ID of the object it is being called on, instead it supplies null and I receive the following error message:
SQLSTATE[23000]: Integrity constraint violation:
1048 Column 'link_id'
cannot be null (SQL: insert into 'event_link' ('created_at', 'event_id',
'link_id', 'updated_at') values (2018-06-09 11:27:15, 2, , 2018-06-09 11:27:15))
I've triple checked my models and database structure.
I can't even imagine how this error could occur since the id lacking in the SQL query is the id of the object that the attach() method is actually being called on. If I use sync($eventID, false) instead of attach(), the result is the same.
Event table:
Link table:
Event_Link table:
The following is the problematic method in the controller responsible for storing the record and creating an entry in the event_link weak entity.
The $link object is created successfully if the attach() line is commented out, a JSON representation of a link is returned which confirms this (but it lacks the 'id' field).
public function store(StoreLink $request) {
$link = Link::create([
'title' => $request->title,
'url' => $request->url,
]);
if ($request['eventId']) {
// $request->eventId is passed successfully, $link id is not passed.
$link->events()->attach($request->eventId);
}
return response()->json($link, 201);
}
Link Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Link extends Model
{
protected $table = 'links';
public $incrementing = false;
public $timestamps = true;
protected $primaryKey = "id";
protected $fillable = ['title', 'url'];
public function events()
{
return $this->belongsToMany('App\Event')->withTimestamps();
}
}
Event Model:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Event extends Model
{
protected $table = 'events';
public $incrementing = false;
public $timestamps = true;
protected $primaryKey = "id";
protected $fillable = ['description', 'date', 'image', 'category_id'];
public function category()
{
return $this->belongsTo('App\Event', 'category_id');
}
public function links()
{
return $this->belongsToMany('App\Link', 'event_link')->withTimestamps();
}
public function history()
{
return $this->belongsToMany('App\User', 'user_history');
}
public function favourites()
{
return $this->belongsToMany('App\User', 'user_favourites');
}
}
The problem is public $incrementing = false;: Remove the line from both your models.
Your id columns are defined as AUTO_INCREMENT, so $incrementing has to be true.
I'm trying to create a league table in Laravel but I'm running into some issues with guess what, relationships, again. They never seem to work for me in Laravel. It's like they hate me.
I have a modal for matches
<?php
namespace App\Database;
use Illuminate\Database\Eloquent\Model;
class Match extends Model
{
protected $primaryKey = 'id';
protected $table = 'matches';
public $timestamps = false;
protected $guarded = ['id'];
}
And a modal for teams, but with a matches() function
<?php
namespace App\Database;
use Illuminate\Database\Eloquent\Model;
class Team extends Model
{
protected $primaryKey = 'id';
protected $table = 'teams';
public $timestamps = false;
protected $guarded = ['id'];
public function matches() {
return $this->hasMany('App\Database\Match', 'team_one_id, team_two_id');
}
}
I think the issue comes with team_one_id, team_two_id as the teams primary key could be in either one of them columns for the other table. When calling count() on matches() it throws an error.
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'matches.team_one_id, team_two_id' in 'where clause' (SQL: select count(*) as aggregate from matches where matches.team_one_id, team_two_id = 1 and matches.team_one_id, team_two_id is not null)
can you try this syntax
return $this->hasMany('modelPath', 'foreign_key', 'local_key');
Does Match table have a column maned 'team_id'?
because it's the default naming convention in the laravel docs for mapping the tables.
if you do have the column and populate the data you can just remove the foreign & local keys from matches() relationship. you don't need it. Laravel will automatically map it for you.
if you do not have the 'team_id' on Matches table please add the column and add the respective team ids for matches.
<?php
namespace App\Database;
use Illuminate\Database\Eloquent\Model;
class Team extends Model
{
protected $primaryKey = 'id';
protected $table = 'teams';
public $timestamps = false;
protected $guarded = ['id'];
public function matches() {
return $this->hasMany('App\Database\Match');
}
}
This way you can implement it, Add these relationship and a method in Team Model
public function homeMatches() {
return $this->hasMany('App\Database\Match', 'team_one_id');
}
public function awayMatches() {
return $this->hasMany('App\Database\Match', 'team_two_id');
}
public function matches() {
return $this->homeMatches->merge($this->awayMatches);
}
Now Fetch the data
$team = Team::find(1);
$matches = $team->matches(); //now it will fetch all matches for both columns
If you want to fetch matches as attributes then you can add one method
in your Team model
public function getMatchesAttribute()
{
return $this->homeMatches->merge($this->awayMatches);
}
Now you can fetch the matches as $matches = $team->matches;
Here is the difference
$team->matches returns Illuminate\Database\Eloquent\Collection
And
$team->matches() returns Illuminate\Database\Eloquent\Relations\{Relation Name}
You can't use matches in Eager loading like Team::with('matches') because matches is not a relationship and that causing your Error. What you can do is add homeMatches and awayMatches in eager loading and then call $team->matches().
$teams = Team::with('homeMatches', 'awayMatches')->get();
$teams->each(function ($team) {
print_r($team);
print_r($team->matches());
});
I want to select an item from the table board by it's PK which is boardId.
However the Board::find(2) or Board::find(1) returns all items, regardless of given parameter.
I have the following table:
With corresponding Lumen Model:
class Board extends CustomModel
{
protected $fillable = [
'userId', 'boardName', 'private'
];
protected $primaryKey = 'boardId';
protected $table = 'board';
}
Custom Model is:
abstract class CustomModel extends Model
{
use Authenticatable, Authorizable;
const CREATED_AT = 'createdAt';
const UPDATED_AT = 'updatedAt';
}
Calling it with:
$boardId = 2;
return Board::find($boardId)->get();
Also tried:
$boardId = 2;
return Board::find($boardId)->first();
And the following:
$boardId = 2;
return Board::find($boardId)->toSql();
Returns: select * from 'board'. So there is no where boardId = ? or something, which is the problem. What am I doing wrong ?
I'm running PHP 7.2.0 and Lumen 5.5.2. Queries on other models work fine.
You've said you're calling it with get():
return Board::find($boardId)->get();
But you shouldn't add get() because it will return a collection of all objects. Just do this:
return Board::find($boardId);
Just erase the ->get() method this will works for you
I hope be useful
I am having issues getting the relationship array back when eager loading in laravel 4. for example:
controller:
foreach (Apps::with('extra')->get() as $app)
{
print_r($app->toArray());//returns array but my relationship array at the bottom says null
echo $app->extra; //this will show my relationship details
}
model:
class Apps extends Eloquent
{
protected $connection = 'mysql_2';
protected $table = 'apps';
public $timestamps = false;
protected $primaryKey = 'name';
public function host()
{
return $this->belongsTo('Hosts','name');
}
public function extra()
{
$this->primaryKey='app_ip';
return $this->hasone('Extra','ip');
}
//other functions below.......
}
class Extra extends Eloquent
{
protected $connection = 'mysql_3';
protected $table = 'extra';
public $timestamps = false;
protected $primaryKey = 'ip';
public function app(){
return $this->belongsTo('Apps', 'app_ip');
}
mysql:
My mysql tables were not created through laravel they were previously existent. the app_ip column in the Apps table relates to the ip column in the extra table. it is a 1 to 1 relationship and I have specified the primary key in the relationship function. I am getting relationships back so I know that it is working.
I am able to get relationship data back when I call the function directly, but it does not show the relationship data when I try and print the full array. The main goal is to be able to return both the relationship columns and the app columns in one response.
You need to do this:
$apps = Apps::all();
$apps->load('extra');
foreach ($apps as $app)
{
print_r($app->toArray()); // prints your relationship data as well
}
What you have should work and iterating through the collection or using ->load() to eager load shouldn't make a difference. Are you using the visible restriction on your models? If so you will need to include the relationships.
class Apps extends Eloquent {
protected $visible = array(
'id',
'name',
'created_at',
'extra', // Make the relationship 'visible'
);
public function extra()
{
return $this->hasMany('Extra');
}
}
Code:
<?php
class Catering extends \Eloquent {
protected $table = 'catering';
public $timestamps = FALSE;
public function offers() {
return $this->hasMany('Offer', 'cid');
}
}
class Offer extends \Eloquent {
protected $table = 'catering_offer';
public $timestamps = FALSE;
public function catering() {
return $this->belongsTo('Catering');
}
}
I am able to do
$offers = Catering::find(1)->offers;
but, the inverse is not working:
$catering = Offer::find(1)->catering;
is always returning NULL. Database has the right values.
Offer table has 2 columns:
primary(id), int(cid)
that references catering.id.
The question:
How can i access the reverse side of this relation?
You said that, I am able to do
$offers = Catering::find(1)->offers;
and in your Catering model you have
public function offers() {
return $this->hasMany('Offer', 'cid');
}
It seems like you've defined a different foreign key here (cid) to use it instead of the default one that laravel basically supposed to use, so, to do the reverse relation you have to do the same thing in your Offer model's catering function
public function catering() {
return $this->belongsTo('Catering', 'cid');
}
In the Laravel Documentation, it says that, you may override the conventional foreign key by passing a second argument to the hasMany method, like
return $this->hasMany('Offer', 'custom_key');
Same way, to define the inverse of the relationship on the Offer model, you can use the belongsTo method, like
return $this->belongsTo('Catering', 'custom_key'); // cid