Eloquent Many to Many attach() is passing a null model ID - php

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.

Related

How can I use soft deleted with relation

I use eloquent with soft delete .I'm getting error because of my query still select data that already use softdelete here is my model
User Model
class User extends Authenticatable
{
use Notifiable, HasRoles, SoftDeletes;
protected $guard_name = 'web';
protected $fillable = [
'username', 'password'
];
protected $dates = ['deleted_at'];
}
for example I've 100 user and I deleted 1 user with softdelete . then I try to
$a = User::all();
dd($a);
I get 99 user . It works! but after I use it relation It doest work here what I do
This is my Parent table and Model
table
|id|user_id|parent_id|
Note : user_id and parent_id are FK in user.id table
class Parent extends Model
{
protected $table = 'parent';
public function user()
{
return $this->belongsTo('App\User');
}
}
$getParent = Parent::with('user')->get();
when I dd($getParent); why I still get null data from user_id that I already use soft deleted ?
UPDATE model User : after I put whereNull I still getting user that I alredy soft deleted
public function user()
{
return $this->belongsTo('App\User')->whereNull('users.deleted_at');
}
https://laravel.com/docs/5.7/eloquent#querying-soft-deleted-models
...
public function customerAddress()
{
return $this->hasOne(Addresses::class, "id", "id_address")->withTrashed();
}
...
Ok, here's what I think is going on...
With soft delete the ondelete event doesn't work (meaning that related models is not deleted). I'm not sure if that changed in later versions of Laravel, but I don't think so. Also deleting User would still not affect the parent model, since you haven't defined the relationship between User and Parent (in the User model), only between Parent and User.
Try defining the relationship in User and then override the boot() function, that sits in the Model class. (This is untested code, but something like this should do the job)
class User extends Authenticatable
{
use Notifiable, HasRoles, SoftDeletes;
protected $guard_name = 'web';
protected $fillable = [
'username', 'password'
];
protected $dates = ['deleted_at'];
// Override Model boot function
protected static function boot()
{
parent::boot();
static::deleting(function ($users) {
foreach ($users->parents()->get() as $parent) {
$parent->delete();
}
});
}
// Define relationship with parent model
public function parents()
{
$this->hasMany('App\Parent');
}
}
You can put a constraint on the Eager Load:
public function groups()
{
return $this
->belongsToMany('Group')
->whereNull('group_user.deleted_at') // Table `group_user` has column `deleted_at`
->withTimestamps(); // Table `group_user` has columns: `created_at`, `updated_at`
}
Instead of HARD deleting the relationship using:
User::find(1)->groups()->detach();
You should use something like this to SOFT delete instead:
DB::table('group_user')
->where('user_id', $user_id)
->where('group_id', $group_id)
->update(array('deleted_at' => DB::raw('NOW()')));

Laravel Multiple linking column in another table with relationships?

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());
});

Laravel 4: Save polymorphic relationship

I'm trying to save a polymorphic relationship in laravel and can't get it to work. I either get this error: Call to undefined method Illuminate\Database\Query\Builder::save() if I do $user->provider()->save($provider)
Or I get
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'userable_type' in 'field list' (SQL: insert into 'providers' ('user_id', 'userable_type', 'userable_id') values (16, Provider, 16))
if I switch it around and do $provider->user()->save($provider); as suggested in Call to undefined Builder::save() when saving a polymorphic relationship
From what little documentation I could find on polymorphic relationships, it seems the userable_type/id stuff goes in the parent table, and since User is my parent table, rather than Provider, the 2nd method seems to be the wrong way to do it.
At a guess I'd say the problem is with my morphTo/morphOne methods, but I've no clue how to fix them since the documentation for those methods has nothing to say about their parameters. My provider table might be set up a bit differently from what laravel expects, since it doesn't have an incremental id field as the primary key; the primary key is the foreign key and is named user_id.
This is my register code:
public function postRegister()
{
$input = Input::all();
$user = $this->user;
$validator = Validator::make($input, $user::$rules);
if($validator->passes())
{
$user->username = $input['username'];
$user->email = $input['email'];
$user->password = Hash::make($input['password']);
$user->save();
if($input['account_type'] == 0)
{
$provider = new Provider();
$provider->user_id = $user->id;
//$provider->user()->save($provider);
$user->provider()->save($provider);
}
return 'Success';
}
else
{
return Redirect::back()->withInput($input)->withErrors($validator->messages());
}
}
User model:
<?php
class User extends Validatable implements UserInterface, RemindableInterface
{
use UserTrait, RemindableTrait;
protected $table = 'users';
public function userable()
{
return $this->morphTo();
}
}
Provider Model:
<?php
class Provider extends Validatable
{
protected $table = 'providers';
protected $primaryKey = 'user_id';
public $timestamps = false;
public function user()
{
return $this->morphOne('User', 'userable');
}
}
You can call it like this:
$provider->user()->save($user);

Laravel Eloquent Relationship column not found

Can you help me with this one?
ANSWER MODEL
class Answer extends Eloquent {
protected $primaryKey = 'ID';
protected $table = 'answers';
protected $fillable = array('customerID', 'agentID', 'status', 'date', 'urn_code', 'urn_id');
public function customer(){
return $this->hasOne('Customer');
}
}
CUSTOMER MODEL
class Customer extends Eloquent {
protected $connection = 'mysql';
protected $table = 'leads';
protected $primaryKey = 'cID';
protected $fillable = array('cID','title', 'first_name','last_name','address1', 'address2', 'post_code','city','phone_number');
public function answers() {
return $this->hasMany('Answer');
}
}
ROUTE
Route::get('sales', function(){
$sales = Customer::with('answers')->get()->paginate(15);
foreach($sales as $sale)
echo $sale->last_name . '<br />';
});
and this is my error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'answers.customer_id'
It's exactly how the error says. In your answers table, Laravel is looking for a customer_id column automatically, and it doesn't exist in this case.
If your customer ID column is under a different name, you can specify it as the second parameter in the hasMany() method:
public function answers() {
return $this->hasMany('Answer', 'my_column');
}
Also, you should probably be using a belongsTo relationship here, as pointed out by #razor.
Since you are using custom primary keys, you need to specify the local key and the foreign key.
public function answers() {
return $this->hasMany('Answer', 'foreign_key', 'local_key');
}
Probably you'll have to update your Answer model as well (please, check out if you really need a hasOne or a belongsTo relation):
public function customer(){
return $this->belongsTo('Customer', 'foreign_key', 'local_key');
}
You can find more info here.

relationship array null when eager loading in laravel

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');
}
}

Categories