CakePHP3.2: I can't delete dependent associated - cakephp-3.x

I should make a big mistake as I already done this several times, but I really don't understand why deleting associated only partially works:
My entities are declared like that:
class SitemessagesTable extends Table {
public function initialize(array $config) {
$this->table('sitemessages');
$this->displayField('title');
$this->addBehavior('Timestamp');
$this->hasMany('SitemessageTitles', [
'dependent' => true,
]);
}
}
class SitemessageTitlesTable extends Table {
public function initialize(array $config) {
$this->table('sitemessage_titles');
$this->displayField('title');
$this->belongsTo('Sitemessages');
$this->hasOne('SitemessageTexts', [
'dependent' => true,
]);
$this->hasOne('SitemessageImages', [
'dependent' => true,
]);
}
}
class SitemessageTextsTable extends Table {
public function initialize(array $config) {
$this->table('sitemessage_texts');
$this->displayField('text');
$this->belongsTo('SitemessageTitles');
}
}
class SitemessageImagesTable extends Table {
public function initialize(array $config) {
$this->table('sitemessage_images');
$this->displayField('image');
$this->belongsTo('SitemessageTitles');
}
}
So I try to delete a Sitemessages entity like that:
$sitemessage = $this->Sitemessages->get($sitemessage_id);
$this->Sitemessages->delete($sitemessage);
Then Sitemessages and first level SitemessageTitles entites are deleted but not the second level SitemessageTexts nor SitemessageImages ones.
I don't understand as the doc says:
When deleting entities, associated data can also be deleted. If your HasOne and HasMany associations are configured as dependent, delete operations will ‘cascade’ to those entities as well.
Any idea?
Regards,

It seems that the docs are a little unclear about the dependent option, as it will only affect the first level association. If you want to have fully cascading deletes, you need to enable the cascadeCallbacks option too.

Related

Eager Loading vs Lazy Loading with API Resource

Could you please suggest which option I should use for the following example.
I'm currently building an API to get Places, Place model contains User and Location models.
Relationships are set like that:
class Place extends Model
{
protected $guarded = [];
public function createdBy()
{
return $this->belongsTo(User::class, 'created_by', 'id');
}
public function location()
{
return $this->belongsTo(Location::class);
}
}
Option 1: Lazy Loading:
public function getPlaces()
{
return Place::all()->get();
}
class PlaceResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'createdBy' => (new UserResource($this->createdBy)),
'location' => (new LocationResource($this->location)),
];
}
}
Option 2: Eager Loading:
public function getPlaces()
{
return Place::with([
'createdBy',
'location',
])
->get();
}
class PlaceResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'createdBy' => (new UserResource($this->createdBy)),
'location' => (new LocationResource($this->location)),
];
}
}
Logically thinking with the Eager Loading option data should loads quicker, right? I have tested both options for about 100 places but the loading time seems to be the same.
What's the right way (option to use) to load data quicker?
It is better to Eager Load those relationships when dealing with a result set like that. Since you are iterating through each record in that result set and accessing the relationship this would cause a N+1 issue otherwise. Since it is 2 relationships being accessed it would be (N*2) + 1 .

How to filter/select on intermediate (pivot) table columns in Laravel (5.4)

How do I create a column just in a pivot (intermediate) table in Laravel (5.4) and then filter results on it?
I have two models, Films and CastAndCrew. CastAndCrew are the various directors, producers, actors who work on a film. The pivot table should define the type of relationship between a CastAndCrew member and a Film. Obviously it's possible for someone to be e.g. an actor in one film and a producer on another, so I can't define this in their entry in the CastAndCrew table because it'll only be true for one film, and may be different for other films they worked on. So I assume I have to define the relationship in a pivot table, but I'm not sure how to do this exactly. What I've got so far:
class Film extends Model
{
protected $fillable = array('filmtitle', 'description');
public function List_Directors()
{
return $this->belongsToMany('App\CastAndCrew')->withPivot('type')->wherePivot('type', 'director');
}
public function List_Actors()
{
return $this->belongsToMany('App\CastAndCrew')->withPivot('type')->wherePivot('type', 'actor');
}
}
and
class CastAndCrew extends Model
{
protected $fillable = array('firstname', 'lastname');
public function List_Films_With_Director()
{
return $this->belongsToMany('App\Film')->withPivot('type')->wherePivot('type', 'director');
}
public function List_Films_With_Actor()
{
return $this->belongsToMany('App\Film')->withPivot('type')->wherePivot('type', 'actor');
}
}
When new CastAndCrew members get added to the site, I'm intending to use the attach method, e.g. to add a new director:
$newcastcrew->CastAndCrew::create(['firstname' => Request::get('firstname'), 'lastname' => Request::get('lastname')]);
$newcastcrew->List_Films_With_Director()->attach($filmID, ['type' => 'director']);
1.) Is that right?
2.) Does the ->withPivot('type') create the 'type' column in the Pivot table? If not, where/how do I define it?
2.) Presumably the ->wherePivot('type', 'director') clause in Film->List_Directors() then returns CastAndCrew members who are directors of that film? (which is what I want)
Corrections much appreciated!
Thanks
Your idea and logic is perfectly fine. You might want to add a relationship without the type condition to fetch all the films of user and all the cast and crew of a film. You also need to name your methods and relationships better. I've cleaned up the code for you. Feel free to use this if you prefer.
class Film extends Model
{
protected $fillable = array('filmtitle', 'description');
public function castAndCrew()
{
return $this->belongsToMany('App\CastAndCrew')->withPivot('type');
}
public function directors()
{
return $this->castAndCrew()->wherePivot('type', 'director');
}
public function actors()
{
return $this->castAndCrew()->wherePivot('type', 'actor');
}
}
class CastAndCrew extends Model
{
protected $fillable = array('firstname', 'lastname');
public function films()
{
return $this->belongsToMany('App\Film')->withPivot('type');
}
public function filmsAsDirector()
{
return $this->films()->wherePivot('type', 'director');
}
public function filmsAsActor()
{
return $this->films()->wherePivot('type', 'actor');
}
}

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

Laravel: seeding tables other than Users

UPDATE: I am going to include my full file replacing the partial view I had. The seeder for the User table works, but the one for the Groups table does not. I do have those tables produced by Sentry but I only created a Model for Groups that has nothing in it other than the declaration of the class. Don't know what else to include.
<?php
class DatabaseSeeder extends Seeder {
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
Eloquent::unguard();
//User::create(array('email' => 'foo#bar.com'));
// $this->call('UserTableSeeder');
$this->command->info('User table seeded!');
}
}
class UserTableSeeder extends Seeder {
public function run()
{
User::create(array(
'username' => 'alvaro',
'permissions' =>'{"user":1}'
));
$this->command->info('User table seeded!');
}
}
class GroupTableSeeder extends Seeder {
public function run()
{
Group::create(array(
'name' => 'usuario',
'permissions' =>'{"user":1}'
));
$this->command->info('Group table seeded!');
}
}
But actually, the one I want is the Groups tables (I am on Sentry). Yes, I have created the Model for Group, as Group.php but I don't know how to define its contents. Sometimes I have seen on other occasions that it suffices with just defining the class, but here I dont know, it doesn't work that easily.
Just doing something like
class GroupTableSeeder extends Seeder
will not work as it says that such class does not exist.
The only thing I needed to do was to create a separate file having that name GroupTableSeeder.php
and include the code in there. For some reason, while UserTableSeeder can be inside a file called DatabaseSeeder and it works, it does not work for other tables.
class GroupTableSeeder extends Seeder {
public function run()
{
Group::create(array(
'name' => 'basicuser',
'permissions' =>'{"user.create" :-1,"user.delete" :-1,"user.view":1,"user.update":-1,"post.create":1}'
));
$this->command->info('Group table seeded!');
}
}

How to create self referential relationship in laravel?

I am new to Laravel. I Just want to create a self referential model. For example, I want to create a product category in which the field parent_id as same as product category id. How is this possible?
Model Shown below
class Product_category extends Eloquent
{
protected $guarded = array();
public static $rules = array(
'name' => 'required',
'parent_id' => 'required'
);
function product_category()
{
return $this->belongsto('Product_category','parent_id');
}
}
It results Maximum function nesting level of '100' reached, aborting! Error
You can add a relation to the model and set the custom key for the relation field.
Update:
Try this construction
class Post extends Eloquent {
public function parent()
{
return $this->belongsTo('Post', 'parent_id');
}
public function children()
{
return $this->hasMany('Post', 'parent_id');
}
}
Old answer:
class Post extends Eloquent {
function posts(){
return $this->hasMany('Post', 'parent_id');
}
}
Your model is not at fault for producing the "maximum function nesting level of '100' reached" error. It's XDebug's configuration; increase your xdebug.max_nesting_level.
The following is from a 2015 post by #sitesense on laracasts.com:
This is not a bug in Laravel, Symfony or anything else. It only occurs when XDebug is installed.
It happens simply because 100 or more functions are called recursively. This is not a high figure as such and later versions of XDebug (>= 2.3.0) have raised this limit to 256. See here:
http://bugs.xdebug.org/bug_view_page.php?bug_id=00001100
EDIT: In fact the latest Homestead provisioning script already sets the limit to 250. See line 122 here:
https://github.com/laravel/settler/blob/master/scripts/provision.sh#L122
So the addition of xdebug.max_nesting_level = 250 to php.ini should do it.
I've added a little more to the code based on your comments trying to access the parent!
class Person extends \Eloquent {
protected $fillable = [];
var $mom, $kids;
function __construct() {
if($this->dependency_id<>0) {
$this->mother->with('mother');
}
}
public function children() {
$children = $this->hasMany('Person','dependency_id');
foreach($children as $child) {
$child->mom = $this;
}
return $children;
}
public function mother() {
$mother = $this->belongsTo('Person','dependency_id');
if(isset($mother->kids)) {
$mother->kids->merge($mother);
}
return $mother;
}
}
Then you can access the parent from the child with eager loading, see more here: http://neonos.net/laravel-eloquent-model-parentchild-relationship-with-itself/
you can refer self, using $this
class Post extends Eloquent {
function posts(){
return $this->hasMany($this, 'parent_id');
}
}
Take a look at my answer here.
The key is this code below in Model.php
public function children()
{
return $this->hasMany(Structure::class, 'parent_id')->with('children');
}

Resources