Laravel custom pivot with incrementing key observer event error - laravel

I have a custom pivot model with an incrementing id. I have set up an Observer, and I get events for things like attach, updateExistingPivot, etc. However, the object that is passed into the observer functions does not include the 'id' field.
This gives me an error when I try to save a related model to the custom pivot (Audit model below).
class Audit extends Model
{
public function auditable()
{
return $this->morphTo();
}
}
class User extends Model
{
public function accounts()
{
return $this->belongsToMany('App\Models\Account')
->withPivot('id')
->using('App\Models\AccountUser');
}
}
class Account extends Model
{
public function users()
{
return $this->belongsToMany('App\Models\User')
->withPivot('id')
->using('App\Models\AccountUser');
}
}
class AccountUser extends Pivot
{
public $incrementing = true;
public function account()
{
return $this->belongsTo('App\Models\Account');
}
public function user()
{
return $this->belongsTo('App\Models\User');
}
public function audits()
{
return $this->morphMany('App\Models\Audit', 'auditable');
}
}
If I do
$user->accounts()->updateExistingPivot($acct_id, ['description' => $description]);
an updating event fires, but it does not contain in id:
(
[user_id] => 18000
[account_id] => 111389
[description] => My Description
)
Then if I try to save a new Audit to the custom pivot, I get an error because there is no auditable_id for the SQL statement:
public function updating(AccountUser $auditable)
{
$audit = new Audit();
...
$auditable->audits()->save($audit);
}
Generates
Integrity constraint violation: 1048 Column 'auditable_id' cannot be null (SQL: insert into `audits` (`auditable_id`, `auditable_type`, `updated_at`, `created_at`) values (?, App\\Models\\AccountUser, 2020-04-02 13:24:53, 2020-04-02 13:24:53))
Any help will be appreciated.

Turns out this was fixed in the Laravel 7.x branch, while I am still on 6.x.
i.e. in 7.x, the custom pivot events will have the id field (and all others) if it exists.

Related

Adding and saving fields in a related belongsToMany table

The tables category, category_description and descriptions are related:
public function descriptions(): BelongsToMany
{
return $this->belongsToMany(Description::class);
}
public function categories(): BelongsTo {
return $this->belongsTo(Category::class);
}
public function descriptions(): BelongsTo {
return $this->belongsTo(Description::class);
}
public function descriptions(): BelongsToMany
{
return $this->belongsToMany(Category::class);
}
in Model respectively. When saving or updating:
public function createOrUpdate(Category $category, Request $request)
{
$category->fill($request->get('category'))->save();
$category->descriptions()->syncWithoutDetaching(
$request->input('category.descriptions', [])
);
}
An error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'description' in 'field list' (SQL: insert into `category_description` (`category_id`, `description`, `description_id`, `is_active`, `meta-description`, `meta-h1`, `meta-keyword`, `meta-title`, `name`, `slug`) values (1, 41231231, 0, 1, 23, 124, 12, 12, 12333312, 74))
Perhaps I missed something somewhere, since there is not so much experience.
UPDATE:
a category can have multiple entries, but the description has only one parent. Rewrote — One To Many (Polymorphic):
public function descriptions()
{
return $this->morphMany(Description::class, 'descriptable');
}
public function descriptable()
{
return $this->morphTo();
}
There are no problems with saving 1 record, but how to update several records at the same time?
How about?
// Category Model.
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
public function descriptions()
{
return $this->belongsToMany(Description::class)
->using(CategoryDescription::class);
}
}
// Description Model.
use Illuminate\Database\Eloquent\Model;
class Description extends Model
{
protected $fillable = [
"name",
"description",
"meta-title",
"meta-description",
"meta-keyword",
"meta-h1",
"slug",
"is_active",
];
public function categories()
{
return $this->belongsToMany(Category::class)
->using(CategoryDescription::class);
}
}
// Intermediate Model.
use Illuminate\Database\Eloquent\Relations\Pivot;
class CategoryDescription extends Pivot
{
protected $table = "category_description";
public $incrementing = true;
public function category()
{
return $this->belongsTo(Category::class, "category_id", "id");
}
public function description()
{
return $this->belongsTo(Description::class, "description_id", "id");
}
}
// createOrUpdate method.
public function createOrUpdate(Category $category, Request $request)
{
$category->fill($request->get('category'))->save();
$description = Description::create(
Arr::collapse($request->input('category.descriptions', []))
);
$category->descriptions()->syncWithoutDetaching(
$description->id
);
}
Notes:
Much as this may work for you, I personally think that you don't have a many-to-many relationship here. I believe a one-to-many relationship is sufficient.
The problem is you send data to be inserted in columns that are not found
You should send only the data that you need to insert in the table
so in your case, you should write your function as
$category->descriptions()->syncWithoutDetaching($description_id); // the id of the description you want to attach with this category
If you still don't have the description yet in the database and you are creating it with the same request you can do something like this
Description::create(['columnName'=>$request->get('columnName'),'columnName2'=>$request->get('columnName2')])->id

Custom Pivot Table name giving error no such table:main

Implementing many to many relationships with custom Pivot table name is giving error.
Service.php Model
class Service extends Model
{
public function categories()
{
return $this->belongsToMany('App\ServiceCategory', 'category_service');
}
}
ServiceCategory.php Model
class ServiceCategory extends Model
{
public function services()
{
return $this->belongsToMany('App\Service', 'category_service');
}
}
Table Names
services
service_categories
category_service (Pivot Table Name)
Test
class RelationshipTest extends TestCase
{
use RefreshDatabase;
/** #test */
public function a_service_can_belong_to_many_categories()
{
$service = factory(Service::class)->create();
$category = factory(ServiceCategory::class)->create();
$service->categories()->sync($category);
$this->assertEquals(1, $service->first()->categories()->count());
$this->assertInstanceOf('Illuminate\Database\Eloquent\Collection', $service->categories);
}
}
The test is giving this error.
PDOException: SQLSTATE[HY000]: General error: 1 no such table: main.categories
How to use custom pivot table name without getting this error.
I had the same error message and the issue came from my migration's foreign key constrains.
I forgot to override the default constrained() table name ("categories") to this:
Schema::create('category_service', function (Blueprint $table) {
// ...
$table->foreignId('category_id')->constrained('service_categories')->onDelete('cascade');
// ..
});

I many-to-many relationship, get ID of pivot table

I have the following models in a many-to-many relationship:
class Event extends Model
{
public function positions() {
return $this->belongsToMany(Position::class, 'position_events');
}
}
class Position extends Model
{
public function events() {
return $this->belongsToMany(Event::class, 'position_events');
}
}
class PositionEvent extends Model
{
public function position() {
return $this->hasOne(Position::class, 'id', 'position_id');
}
public function event() {
return $this->hasOne(Event::class, 'id', 'event_id');
}
}
The position_events table looks like:
id | event_id | position_id
If $event is an instance of Event, I can get the related positions as:
$event->positions;
This gives me something like the following for each related Position:
{"id":4,"name":"Striker","created_at":"2019-04-02 16:19:57","updated_at":"2019-04-02 16:19:57","pivot":{"event_id":27,"position_id":4}}
Notice the pivot element. It only has event_id and position_id as properties, these are columns from the position_events table. How do I get it to have the id column from that table as well?
Have you tried using withPivot(), for example:
$this->belongsToMany(Position::class, 'position_events')->withPivot('id');

How to define this hasManyThrough relation?

I have the following models. Event belongs to a Casefile. Casefile and User are many-to-many.
class Casefile extends Model
{
public function users()
{
return $this->belongsToMany(User::class)->withTimestamps();
}
public function events()
{
return $this->morphMany('App\Event', 'casefile');
}
}
class User extends Authenticatable
{
public function casefiles()
{
return $this->belongsToMany(Casefile::class)->withTimestamps();
}
}
class Event extends Model
{
public function casefile()
{
return $this->belongsTo('App\Casefile');
}
public function users()
{
return $this->hasManyThrough('App\User', 'App\Casefile');
}
}
When I try to:
App\Event::find(526)->users()->get();
It gives:
Illuminate/Database/QueryException with message 'SQLSTATE[42S22]:
Column not found: 1054 Unknown column 'casefiles.event_id' in 'field
list' (SQL: select users.*, casefiles.event_id from users
inner join casefiles on casefiles.id = users.casefile_id
where casefiles.event_id = 526)'
How can I define the "Event has many Users" relation through Casefile?
It's not possible to use HasManyThrough here without a pivot model for the casefile_user table.
You can define a BelongsToMany relationship instead:
public function users()
{
return $this->belongsToMany('App\User', 'casefile_user', 'casefile_id', null, 'casefile_id');
}
If you not followed laravel naming convention for column name. Then you can specify your column name like this
public function users()
{
return $this->hasManyThrough(
'App\User',
'App\Casefile',
'event_id', // Foreign key on casefiles table...
'user_id', // Foreign key on users table...
);
}
For more Laravel HasManyThrough Relationship

Eloquent/Laravel5 linking distant relations ("hasOneThrough")

The question in short:
"pages" and "tasks" have a many-to-many relationship linked by the pivot table "page_tasks". The table "responses" is linked to that pivot table by the foreign key "page_task_id".
Now I want to be able to access the page and the task a response belongs to directly with Eloquent. However the hasManyThrough function does not work, as it exspects the foreign_keys at different places:
public function task(){
return $this->hasManyThrough('PageTask', 'Task', 'page_task_id', 'task_id');
}
Unknown column 'tasks.page_task_id' in 'field list'
This means that eloquent exspects the task table having a foreign key page_task_id pointing to page_tasks. But in my model the page_tasks table has a foreign key task_id pointing to tasks. How do I tell eloquent that fact?
An other approach I tried was to use existing relations that were previously defined:
public function task(){
return $this->page_task->task();
}
This however tells me that there is no methoid called "task".
What would the recommended way be to achieve this? What am I doing wrong?
Here are some more details if needed:
"pages" and "tasks" have a many-to-many relationship with pivot table page_tasks linking it.
Page-Model:
class Page extends Model {
public function tasks(){
return $this->belongsToMany('Task', 'page_tasks');
}
}
Task-Model:
class Task extends Model {
public function pages()
{
return $this->belongsToMany('Page', 'page_tasks');
}
}
This works fine.
Response-Model looks like this
class Response extends Model {
protected $fillable = [
'page_task_id',
];
public function page_task(){
return $this->belongsTo('App\PageTask', 'page_tasks');
}
public function task(){
??????
}
}
PageTask-Model looks like this:
class PageTask extends Model {
protected $fillable = [
'page_id',
'task_id',
];
public function page(){
return $this->belongsTo('Page');
}
public function task(){
return $this->belongsTo('Task');
}
public function responses(){
return $this->hasMany('Response');
}
}

Resources