Laravel polymorphic relations / model observers and database integrity - php

What is the best practice to keep database integrity while using laravel's polymorphic relations?
I'm using Model Observers to update/delete related models. So for example I delete related Documents on "deleted" event while deleting a Customer.
That means if an error occurs while deleting first document the rest will stay in the database... Or if I wrap documents deleting in a transaction all of them will stay in the database while parent object is deleted...

Unfortunately, there is no good answer for that problem. You can not keep your database integrity with foreign keys because of the polymorphic relation.
One way to try is to use database triggers. As Laravel polymorphic system keep a "type" column on the table that references the related model, you could put a "before delete" trigger on tables that can be morphed to delete all occurences that references the raw you want to delete.
For example, with the structure used in Laravel documentation :
staff
id - integer
name - string
orders
id - integer
price - integer
photos
id - integer
path - string
imageable_id - integer
imageable_type - string
You can put an "before delete" trigger on staff table that executes a query (PostgreSQL syntax here) like that :
DELETE FROM photos WHERE imageable_id = OLD.id AND imageable_type = 'staff'
That way, each time you delete a staff raw, all referenced photos will be deleted in the same transaction.
I really don't know if it's the best way to keep integrity with this kind of relation, but it seems to be an efficient one.

Related

Should I include auto-incremental id in all related tables?

I have multiple tables in a Laravel app with 1-to-1 relationship such as users , users_settings , user_financial
And some 1-to-many relationships such as users_histories
My questions are:
1. Should I always include incremental id at the first?
for example is the id necessary in the Table #2 below?
Table 1:
id (primary,increments) , name, email, password
Table 2:
id (primary,increments), user_id, something_extra
^ why does every guide include this? // e.g. https://appdividend.com/2017/10/12/laravel-one-to-one-eloquent-relationships/
Can't I just use user_id as primary key and skip the incremental key? because I want to auto insert it on table 2 as soon as data is inserted in table 1.
2. How should I name 1-to-1 and 1-to-many tables in Laravel? `
I searched but didn't find any naming convention for different type of relationships...
Currently I do:
users table with primary key id is the base.
1-to-1: users_settings with foreign key user_id
1-to-many: users_histories foreign_key user_id
many-to-many: users_groups foreign_key user_id
should the first two tables be named settings/setting , histories/history instead? sorry I'm a little confused here.
I actually asked a similar question around 2 days ago. Its up to you but I'd say yes. In my case if I don't auto_increment all my ids in the related tables, data won't be associated with the correct user. However, there is an argument for saying auto_increment columns should not be used in this case, but they are useful for other things. According to some, the relationships might not be as meaningful so it'd be up to you and down to the specifics of you data tables for how meaningful the relationship will be. Regardless, you should research more into the advantages of auto_incrementing all your ids in related tables, as well as possible disadvantages before deciding what you want to do. Either way is fine, but they offer different advantages and disadvantages- which you'll need to compare and what works best for your specific case.
This is a well debated topic about the primary key. IMHO, No, you shouldn't. Every column in database should have a purpose. Following this, for your example, I agree that the auto_increment id is redundant and this is simply because it doesn't have a purpose. The second table is still uniquely describing the user so that the primary key should be the user_id.
Beside the above, there is another principle for me to decide whether I need the auto_increment id: whether I can see a table as an entity. For example, user is clearly an entity, but a relationship is not (in most cases), i.e., composite key can serves the purpose. But when an relationship table is extended to have more attributes and it starts to make sense for it to have an auto_increment id.
I don't have much experience on Laravel, but the naming for a database table should not be dictated by a framework. Comparing history and user_history, what a new DBA or developer expect from the two names without looking its data? user_history describes the table more precisely

Doctrine: Avoid duplicates in same insert process

Is there a built in way / recommendation to handle duplicate entries while entities are inserted without a flush for each entry?
Lets assume we have a person entity with two fields. ID and personalNr.
ID is the primary key but personalNr has also a unique constraint.
Now i am iterating over a csv file where one personalNr can be listed in multiple rows.
If the row contains a personalNr, I check against the repository if a entry with this personalNr exist. If yes, i link the dataset to the person with the personalNr. If no, i create a new person with the personalNr and link it afterwards with the dataset.
The problem now is:
If a personalNr occurs more than once in a csv file, the dataset already exists in the entity manager when i find it again, but its unsaved to the database yet (i called no flush until now). The check against the repository will say "There is no entity in the repository" but the entity manager will throw an duplicate entry exception on flush cause it tries to create multiple entities with the same personalNr.
How to avoid this? Is the only way to save what was already inserted or not by myself? Has doctrine such a checking mechanism?
Thanks

Laravel: Deleting Eloquent aggregates

I'm looking for a strategy for destroying Eloquent aggregates, e.g. models that "contain" other models.
In Ruby on Rails's ActiveRecord, there is a dependent: :destroyoption that can be placed when defining relations, meaning that if e.g. an order is deleted, then the order_lines must go too.
Is there something similar in Laravel? Or any other alternatives besides manually cleaning up and wrapping it in a transaction?
If you use Laravel schema builder, you should use foreign key and for foreign key add for example:
$table->foreign('entry_id')
->references('id')
->on('entries')
->onDelete('CASCADE');
You can of course do the same in for example phpMyAdmin.
When using such construction, if in this case record from entries will be deleted, automatically record from this table that have entry_id the same as id from entries that is being delete will be deleted.
It's not a trigger, because trigger are much more complex structure, it's just defining relation and telling what to do if related parent record will be deleted.

How to implement non explicit 1:1 relationship with Doctrine?

I have two tables, Inventory and, say, Stuff. Inventory is used to store data common to Stuff and other tables. The way the DBA envisioned this working would be with us inserting the Inventory table and then using the generated ID to insert the Stuff table.
How can I implement this scenario using Doctrine 2? I'm tempted to just add a 1:1 relationship on the model but I'm not sure I can convince the DBA to change the database.
With the workaround described here http://www.doctrine-project.org/docs/orm/2.0/en/reference/limitations-and-known-issues.html#foreign-keys-as-identifiers you should be able to get the DBAs schema working. With version 2.1 of Doctrine (or the current master) you can use the new foreign key as identifier feature to get it working.
However if you are not using Sequences of Oracle/Postgresql you need to flush operations for this (persist parent, flush, associate and persist child, flush)

How do I specify a mysql cascade rule for the following

In mysql, I've got the following:
TABLE docs (
ID
title
content
)
TABLE specialDocs (
docID -> docs(ID)
userID -> users(ID)
)
So specialDocs is a subset of documents that can belong to users.
I am wondering: Is it possible to specify a cascade rule so that when a user is deleted, the docs that belong to that user are automatically deleted? (I realize it would be simple to do this if the pointer to users(ID) was a column within the table "docs". However I am unclear if it is possible with a join table like the above...)
Thanks in advance for your help
I wouldn't do that - your specialdocs table is a many-to-many table, so multiple users could be related to the same document. What you want, shouldn't be allowed without deleting the other references first - which a cascade delete won't do. And if you don't have referential integrity (IE: MyISAM table), then you'll have orphaned records in specialdocs to a document that no longer exists.
Cascade on delete is a referential integrity feature, so it won't be available for MyISAM tables - you'd have to use a trigger for that. Given the need to check for referential dependencies, I'd use a trigger for Innodb tables as well to get rid of the related specialdocs records first...

Categories