简体   繁体   中英

Saving HABTM with extra fields?

I am trying to save an order, and the products in the order.

The order is being saved, but the products are not.

I have an orders table and a products table and a orders_products table.

In the Order model I set $hasAndBelongsToMany = 'Product';

on the orders_products table I have a couple extra fields: order_id , product_id plus price , quantity to capture the sale price and quantity sold.

I am saving the data via:

$this->Order->saveAll($data);

Here is what $data is:

Array
(
    [Order] => Array
        (
            [user_email] => st@kr.com
            [billing_first] => Steve
            ... //more excluded
            [total] => 5000
        )

    [Product] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [price] => 5000.00
                    [quantity] => 1
                )

        )

)

The order gets saved to the order table but nothing is getting saved to the orders_products table. I am expected the orders_products table to save [new_order_id], 1, 5000.00, 1

I do get this notice:

Notice (8): Undefined index: id [CORE/cake/libs/model/model.php, line 1391]

Model::__saveMulti() - CORE/cake/libs/model/model.php, line 1391
Model::save() - CORE/cake/libs/model/model.php, line 1355
Model::__save() - CORE/cake/libs/model/model.php, line 1778
Model::saveAll() - CORE/cake/libs/model/model.php, line 1673
CartsController::saveOrder() - APP/controllers/carts_controller.php, line 128
CartsController::checkout() - APP/controllers/carts_controller.php, line 172
Dispatcher::_invoke() - CORE/cake/dispatcher.php, line 204
Dispatcher::dispatch() - CORE/cake/dispatcher.php, line 171
[main] - APP/webroot/index.php, line 83

Any ideas?

HABTM is over-sold. A lot of the times it fails to meet the needs, such as when you have additional data to store. You'll be better off to do a hasMany/belongsTo relationship between the models.

Taken from the CakePHP Book:

What to do when HABTM becomes complicated?

By default when saving a HasAndBelongsToMany relationship, Cake will delete all rows on the join table before saving new ones. For example if you have a Club that has 10 Children associated. You then update the Club with 2 children. The Club will only have 2 Children, not 12.

Also note that if you want to add more fields to the join (when it was created or meta information) this is possible with HABTM join tables, but it is important to understand that you have an easy option.

HasAndBelongsToMany between two models is in reality shorthand for three models associated through both a hasMany and a belongsTo association.

In your case I would suggest making a LineItem model and joining everything that way:

  • Order hasMany LineItem
  • LineItem belongsTo Order , Product

Well HATBM works in an very strange way. If you really need HATBM I would suggest NOT changing a model association, but using an interesting shorthand for saving additional data fields in the join table.

I've encountered this a lot of times and this is the best solution: Actually, just for saving your additional fields you can unbind all HATBM relations and add a new hasMany binding "on the fly" pointing to the join model. Firstly you should unbind the existing HATBM relation.

$this->Order->unbindModel(array('hasAndBelongsToMany' => array('Product')));

Then add a new "on-the-fly" hasMany binding:

$this->Order->bindModel(array('hasMany' => array('OrdersPost')));

Then if your data is:

$this->data['Order']['id'] = '1';
$this->data['OrdersPost'][0]['product_id'] = '15';
$this->data['PostsTag'][0]['price'] = '5000.00';
$this->data['PostsTag'][0]['quantity'] = '1';
$this->data['OrdersPost'][1]['product_id'] = '16';
$this->data['PostsTag'][1]['price'] = '4000.00';
$this->data['PostsTag'][1]['quantity'] = '2';

As mentioned above, HATBM always erases all existing records before inserting new ones. So maybe you should take care about that before you do:

$this->Order->saveAll($this->data);

This works when you dont want to change your datamodel, but still need additional functionality. Hope this helps. Original Solution and Credits HERE

Another problem I usually encounter with using SaveAll is that it doesn't save the related records. In your example, the Order is saved but the OrderProducts(or OrderItems) are not saved. What I usually do is something like this:

if ($this->Order->save($this->data)) {
    for($i=0; $i<sizeof($this->data['OrderProduct']); $i++){
        $this->data['OrderProduct'][$i]['order_id'] = $this->Order->id;
    }
    $this->Order->OrderProduct->saveAll($this->data['OrderProduct']);
}

What happens here is that the Order is saved first, then its ID is copied to each OrderProduct. Then OrderProduct records are saved.

It could be that your orders_products table does not have it's own primary key. You will get this error unless your table looks something like this

CREATE TABLE orders_products (
  id integer,
  order_id integer,
  product_id integer,
  ...

I only just now had this same problem. Sorry such a simple answer did not get to you sooner.

Saving that additional data in your orders_products table doesn't seem like a good idea to me. What if a order has more than 1 product. Do you store the total amount of the order for each entry in the orders_products table? Why don't you just store that information in the Order ? That makes much more sense to me.

Your error seems to come from a broken data model.

You can use the previous solution, BUT if have also to update the parent tables, you must unbind. In my example I have a table Order and a table Product . I needs to put two extra fields for every product with respect to the Order.id . See this example:

    $this->Order->create(); 
    $this->Order->unBindModel(array('hasAndBelongsToMany'=>array('Product')));
    $f  = $this->Order->save($this->data,false);

    /* Save extra columns in HABTM TABLE */
    $this->Order->bindModel(array('hasMany'=>array('OrderProduct')));
    $q = array();
    if (!isset($this->data['Product']))
    {
        $v  = false;
    }
    else
    {
        $v =true;

        for($i=0;$i<count($this->data['Product']);$i++)
        {
            $this->data['OrderProduct'][$i]['order_id']     = $this->Order->getLastInsertID();
            $this->data['OrderProduct'][$i]['product_id']   = $this->data['Product'][$i];
            $this->data['OrderProduct'][$i]['quantity']         = $this->data['quantity'][$i];
            $this->data['OrderProduct'][$i]['state_id']         = $this->data['State'][$i];
        }
    }

    $s  = $this->Order->OrderProduct->saveAll($this->data['OrderProduct']);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM