Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Help me please with recursive deletion
  • Have this table structure:

    resturants has-many orders
    - many-to-many vegetables
    or
    - has-many ordervegetables belongs-to vegetables.

    When I remove restaurant i do this:

    foreach ($restaurant->orders as $order) {
           foreach ($order->ordervegetables as $ordervegetable)
                   $ordervegetable->delete();
           $order->delete();
    }
    $restaurant->delete();


    And in process I got an error message:
    ERROR - 2015-04-28 11:32:08 --> 1062 - Duplicate entry '0-0' for key 'order_vegatable' [ INSERT INTO `orders_vegetables` (`amount`) VALUES ('300') ] in /home/bvn/public_html/vegstore/fuel/core/classes/database/mysqli/connection.php on line 266


    Nothing understand! What the insert query when I only delete?

    Thank you.
  • May be most interesting model is Model_Ordervegetable:

    class Model_Ordervegetable extends \Orm\Model
    {
        protected static $_properties = array(
            'order_id',
            'vegetable_id',
            'amount',
        );
       
        protected static $_primary_key = array('order_id', 'vegetable_id');

        protected static $_belongs_to = array('order', 'vegetable');

        protected static $_table_name = 'orders_vegetables';

    }

    If need others, I post it too.
  • It seemd to insert into orders_vegetables values with both keys order_id and vegetable_id set to 0 when I try to delete order even after all ordervegetables records deleted... why?
  • Ok... It's very strange ORM behavior... In Kohana ORM I don't remember same troubles... Now I try rewrite all of this with query builder... have no time to bang the head at the wall ;)
  • ORM will never insert records on a delete, so you must be doing something funny.

    What can happen is that an UPDATE query is generated on a related table with 0 as the key, which happens if you define the relation the wrong way around.

    Besides that, if you want to cascade deletes into a relation, simply set 'cascade_delete' to true in the relationship definition, and related child objects will be deleted when you delete the parent. No need for complicated foreach constructions, and potentially hunderds of individual DELETE queries.
  • I think, it is trying to update, but encountered that records are absent, because it insert some new records from current model objects... resurrection :)

    I don't set the 'cascade_delete' or 'cascade_update', because I have not mention of what desirable behavior of this, that is why I left it defaults.
  • Fuel doesn't do that. So it must be something in your code that triggers this behaviour.
  • May be later I will learn this situation in detail.
    I have made two stumbles when debate with you, because I will wait when I will have more time for this and then I will give my answer...

    But, good, suitable and quick ORM in my favorite framework - it is important part of my programming cultivation. Erlier I changed frameworks one by one to find the best, now I have found. That is why, I want to change something, if it not satisfy me... but not changing framework. In general it is very good!
  • I carefully examined this situation and I have to admit, that I am right.

    There is not any hidden framgment of code that can insert the record into DB. What I write in the first post of this discussion is all. Nothing more, but ORM still trying to insert new record.

    I debuged this place and found in the Model class point of bifurcation:

                 // Insert or update
                $return = $this->_is_new ? $this->create() : $this->update();

    It is accurate at row 1300 of code text. Look.
  • It is in method save(), that called from Hasmany::delete() at this place:

    $m->is_changed() and $m->save();

    at row 268.
  • Nothing on a delete can be new, causing an insert. Unless you have assigned it yourself before the delete.

    Again, Fuel doesn't "invent" new records.
  • >>Nothing on a delete can be new, causing an insert.
    Harro, why don't you belive me?

    >>Unless you have assigned it yourself before the delete.
    Only by 'cascade' options, which I don't change and leave defaults. As I understand it try to update, but encountered with absence of records It renew them (in that point of bifurcation, that I adduse).
    Can I change it globally by config?

    >>Again, Fuel doesn't "invent" new records.
    But It does. Do I have to attach video of debuging proccess?

    I wait for collaboration, not confrontation. ORM is very complex for me to make serious changes (may be it is not needed, only I need choose the suitable manner of using it).

    I am going to make video...
  • HarroHarro
    Accepted Answer
    I'm just trying to understand what you do and what happens. I know what the code does.

    I am absolutely sure that the ORM will not INVENT new data. If the ORM decides some object needs saving, it's because it has been changed. If the save results in an INSERT, it is because the object doesn't have a primary key, and is therefore flagged as new.

    The ONLY way such an object can exist, is if you have assigned it yourself to the relation, prior to your deleting action. And then the ORM does what it should to: it will try to save the object you have newly assigned.

    I have watched your recording. It only does the save() call in the has_many relation if the parent has been deleted, but there is no cascade_delete. In that case you have "configured" that the related object should be disconnected from it's parent, in other words have it's foreign key reset (on #266, in the foreach just above the save() call), and this reset needs to be saved.

    That should always result in an UPDATE, not in an INSERT. It can never have an 'is_new' state, again unless your foreign key is also the primary key, in which case the reset has also removed the primary key, and the object is indeed in is_new() state again.

    And that should always work, unless you have made the foreign key also the primary key, which is unsupported in ORM, as that leads to duplicate keys when you have multiple records being disconnected.

    Never define an FK as PK, and if you must, at least always use cascade_delete on the relation to avoid having primary keys reset on a delete.
  • I change my orders_vegetables table and Model_Ordervegetable... I created id field, fill it with ordered numbers, changed primary key from (order_id, vegetable_id) to (id), create unique index on (order_id, vegetable_id) and try again with my deletion code as is.

    And now I've got new error:

    ERROR - 2015-05-07 12:57:44 --> 1048 - Column 'order_id' cannot be null [ INSERT INTO `orders_vegetables` (`order_id`, `vegetable_id`, `amount`) VALUES (null, '1', '100') ] in /home/bvn/public_html/vegstore/fuel/core/classes/database/mysqli/connection.php on line 266

    It is trying to insert again... I don't understand this all, it is too complex for me :-]
  • >at least always use cascade_delete on the relation

    Harro, can I change this globally for defaults behavior?
  • I've understood! :)

    I add to my code two strings (it is for exams, not for common use):

    foreach ($restaurant->orders as $order) {
           foreach ($order->ordervegetables as $ordervegetable)
                   $ordervegetable->delete();
           $order->ordervegetables = null;
           $order->delete();
    }
    $restaurant->orders = null;
    $restaurant->delete();

    It disconnects parent object from his deleted childs and works good, but I agree that it is dumb code :)
    I want to understand how to get an advantage of cascade options usage.

    Harro, please, answer me on that question: Is there the way to change cascade option globally for all application, not for every model relation?

    Or contrariwise change this behavior at certain delete() call.

  • It should disconnect by itself, by updating the foreign key by NULL. And that works fine.

    You got an error that the primary key was reset, and that can't happen, unless you have a FK that is also a PK. Which is a situation that is not supported by ORM (because that means you can't disconnect).

    You're new error happens because you make the same mistake again, but now with the FK. An FK per definition is not unique, so you can't put a UNIQUE index on it. If you do, you can never disconnect (you will have multiple records with a NULL FK), and you have to make sure no two records are linked to the same parent (or in this case, both parents).

    And no, you can not set "cascade_delete" globally. Usually, it is undesired behavior, and child records can exists without being connected to a parent. So you have to define this on a per-relation basis.

    For example, order lines can never exist without an order, so you would cascade the delete on this relation. But you can delete an orderline without deleting the order, so you never cascade on that relation. On the other hand, Classes and Students can exist without being linked to each other (you can have a class without students, and students not going to any class), so a generic definition is dangerous.
    It is something that should follow from your UML, your database design. If your relation is 0::*, do not cascade the delete, if it is 1::*, you should.
  • Thank you, Harro.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion