CakePHP - saving to more than two models using save associated - php

I am trying something more complicated. I have an Item which stores all general items, I have a Product which is an item and I have a Good which is a product and a item. So I have a form for entering value for the good and it shall save to all model related tables (items, products, goods). The reason for so many tables is because all tables shall have an id which is used later, example: product shall have its id which is used later for selling. Here is the controller:
public function add() {
$this->load();
if ($this->request->is('post')) {
$this->Item->create();
$this->request->data['Item']['code'] = $finalCode;
$this->request->data['Item']['is_deleted'] = false;
$item = $this->Item->save($this->request->data);
if(!empty($item)){
$this->request->data['Product']['item_id'] = $this->Item->id;
$this->request->data['Good']['item_id'] = $this->Item->id;
debug($this->request->data['Product']['item_id']);
debug($item);
$this->Item->Product->save($this->request->data);
$this->request->data['Good']['pid'] = $this->Product->id;
$this->Item->Good->save($this->request->data);
}
if($this->Good->validationErrors || $this->Item->validationErrors || $this->Product->validationErrors){
//ERRORS
}
else{
//FAILS
}
}
}
EDIT: I have changed the controller and now the Item is never saved but Product and Good is saved and they are all mapped well, ids are ok but Item is not even in the db, althrough Product and Good have item_id set to a value which should be next in the db.
class Good extends AppModel {
public $belongsTo = array(
'Item' => array(
'className' => 'Item',
'foreignKey' => 'item_id',
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'pid',
)
);
}
class Product extends AppModel {
public $hasOne = array(
'Good' => array(
'className' => 'Good',
'foreignKey' => 'pid',
'dependent' => false,
),
);
}
class Item extends AppModel{
public $hasMany = array(
'Good' => array(
'className' => 'Good',
'foreignKey' => 'item_id',
'dependent' => false,
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'item_id',
'dependent' => false,
),
);
}
Even the debugged $item looks ok but is not saved:
array(
'Item' => array(
'name' => 'Microcontrollers',
'description' => 'Wire Jumpers Female-to-Female 30 cm',
'weight' => '22',
'measurement_unit_id' => '7',
'item_type_id' => '29',
'code' => 'GOD-34',
'is_deleted' => false,
'modified' => '2019-10-22 12:37:53',
'created' => '2019-10-22 12:37:53',
'id' => '120'
),
'Good' => array(
'status' => 'development',
'hts_number' => '8473 30 20',
'tax_group' => '20%',
'eccn' => 'EAR99',
'release_date' => array(
'month' => '10',
'day' => '22',
'year' => '2019',
'hour' => '10',
'min' => '10',
'meridian' => 'am'
),
'is_for_distributors' => '1'
),
'Product' => array(
'project' => 'neqwww'
)
)

I think the problem is with your code at the lines where you are saving the data.
$this->Item->create();
$this->Good->create();
$this->Product->create();
What is $this? If you create a item with "$this" and later try to create a "product", "$this" will not have the item_id.
Try using something like this to save the item created.
$item = $this->Item->create();
Then, with that $item created, you could create a $product with the $item->id
Update:
From cakephp documentation.
// Create: id isn't set or is null
$this->Recipe->create();
$this->Recipe->save($this->request->data);
// Update: id is set to a numerical value
$this->Recipe->id = 2;
$this->Recipe->save($this->request->data);
You must use $this->Item->save($data) to save the information into database.
https://book.cakephp.org/2.0/en/models/saving-your-data.html
Maybe the method create() is a bit unclear. It is used to restart the model state.
So, it would be
$item_saved = $this->Item->save($data['Item']);
$data['Product']['item_id'] = $this->Item->getLastInsertId();
$product_saved = $this->Product->save($data['Product']);
Edit 2:
Maybe it is because you didn't use create() before save the Item. Try this please:
$this->Item->create();
$item_saved = $this->Item->save($data['Item']);
$data['Product']['item_id'] = $this->Item->getLastInsertId();
$product_saved = $this->Product->save($data['Product']);

Related

Creating a User who has-and-belongs-to-many existing Courses with a CakePHP form

I have an app with two associated models: User and Course, which are related by a HABTM association.
There is a registration form where a new user may enter a username and select the courses that they are a part of (from a list of existing courses in the database), only the form only saves the new users - it doesn't save anything to the join table.
The join table (courses_users) has columns course_id and user_id, and the two models look like this:
// User.php
class User extends AppModel {
public $name = 'User';
public $hasAndBelongsToMany = array(
'Courses' => array(
'className' => 'Course',
'joinTable' => 'courses_users',
'foreignKey' => 'user_id',
'associatedForeignKey' => 'course_id'
)
);
}
// Course.php
class Course extends AppModel {
public $name = 'Course';
public $hasAndBelongsToMany = array(
'Users' => array(
'className' => 'User',
'joinTable' => 'courses_users',
'foreignKey' => 'course_id',
'associatedForeignKey' => 'user_id'
)
);
}
In addition, this is the controller action:
// IdentificationController.php
public function register() {
if ($this->request->is('POST')) {
$data = $this->request->data;
$username = $data['User']['username'];
$saved = $this->User->save($data, array('deep' => true));
//debug($data);
if ($saved) {
$this->_set_new_user_session($username);
//$log = $this->User->getDataSource()->getLog(false, false);
//debug($log);
$this->redirect(array('controller' => 'users', 'action' => 'index'));
}
}
// Not redirecting
$courses = $this->Course->find('list', array('Course.name'));
//debug($courses);
$this->set(compact('courses'));
}
And this is the form, sans container divs:
<?php
echo $this->Form->create('User', array(
'inputDefaults' => array(
'label' => false,
'div' => false
),
'url' => '/identification/register'
));
echo $this->Form->input('username', array(
'error' => false,
'autofocus' => true,
'required' => true,
'pattern' => '[a-zA-Z0-9]{3,16}'
));
if ($this->Form->isFieldError('username')) {
echo $this->Form->error('username', null, array('wrap' => 'small', 'class' => 'error'));
}
echo $this->Form->input('Course.Course', array(
'error' => false,
'required' => true
));
if ($this->Form->isFieldError('courses')) {
echo $this->Form->error('course', null, array('wrap' => 'small', 'class' => 'error'));
}
echo $this->Form->button('Register', array(
'div' => false,
'type' => 'submit'
));
echo $this->Form->end();
?>
When I call debug($data), the right data seems to be passed from the form to the controller:
array(
'User' => array(
'username' => 'test63apd'
),
'Course' => array(
'Course' => array(
(int) 0 => '1'
)
)
)
But nothing happens to the join table, and there is no mention of the join table in the DataSource log:
array(
'log' => array(
(int) 0 => array(
'query' => 'BEGIN',
'params' => array(),
'affected' => null,
'numRows' => null,
'took' => null
),
(int) 1 => array(
'query' => 'INSERT INTO `xray2`.`users` (`username`) VALUES ('test06apd')',
'params' => array(),
'affected' => (int) 1,
'numRows' => (int) 1,
'took' => (float) 1
),
(int) 2 => array(
'query' => 'COMMIT',
'params' => array(),
'affected' => (int) 1,
'numRows' => (int) 1,
'took' => (float) 1
)
),
'count' => (int) 3,
'time' => (float) 2
)
Am I missing something really obvious here, or is there some quirk of Cake that I have yet to discover?
You're only telling Cake to save the primary model data. You need to change this line:-
$saved = $this->User->save($data, array('deep' => true));
To:-
$saved = $this->User->saveAssociated($data, array('deep' => true));
saveAssociated() tells Cake to save the current model and its associated data. You also shouldn't need to pass array('deep' => true) as you are only saving data to a directly associated model. So it would be better (and safer) to use:-
$saved = $this->User->saveAssociated($data);
Update
There is an issue with the data being saved as you are not using the alias defined in your association for the data. So when Cake attempts to save the associated data it can't see any. According to your code your User model has and belongs to many Courses (plural) but your save data uses Course (singular). Therefore, your form should be:-
echo $this->Form->input('Courses.Courses', array(
'error' => false,
'required' => true
));
It should be noted that Cake naming conventions use singular forms for model names, so it would be better to use Course in the association rather than Courses. If you change this then your association can be simplified to the following:-
public $hasAndBelongsToMany = array(
'Course'
);
Cake will understand how to handle the join table and foreign keys as they conform to the naming convention. Then you wouldn't need to change your form.

Display a field from a 3rd table

I am trying to display a field from a 3rd table in a relationship, and after checking posts here and the docs, I am still stuck.I have 3 models all related in some way. I have found similar posts here but I am still not getting it to work. I am learning so sorry if I have missed this somewhere in the docs but I have read a fair bit and have tried a lot. I am guessing too much now on this so I need help.
1)Tutorsession - belongsto teachers,
2) Teacher -has 1 user,hasmany tutorsessions
3) User- has 1 teacher, //////I want to display a field from this table given I display tutorsessions
In controller
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => 'User.username'
)
)
); //////////no error but no results
from view echo '<td>'. $item['User']['username'].'</td>'; ///////error user undefined
The tutorsession automatically gets rows from teacher table but not user table witht he model setup on a findall.
I want to display a username from the user table. I display the tutorsession table , I then can display the teacher table with the model association but I cant go from the teacher table to the user table to get a user name from the user id as the common field. I have checked the docs below and I am not sure why my code isnt ble to display a username from users table.
http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html
update: here are the models
class User extends AppModel {
public $hasOne = array(
'Teacher' => array(
'className' => 'Teacher',
'dependent' => true
)
class Tutorsession extends AppModel
{
public $name='Tutorsession';
public $belongsTo = array(
'Teacher' => array(
'className' => 'Teacher',
'foreignKey' => 'teacher_id'
)
class Teacher extends AppModel
{
public $name='Teacher';
public $hasMany = array('Tutorsession');
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
Add containable behavior in your Model
public $actsAs = array('Containable');
Execute following query In controller
$contain = array('Teacher' => array('User'));
$this->set('tutor', $this->Tutorsession->find('first',array(
'conditions' => array('Teacher.user_id' => $id),
'contain' => $contain
)));
Try this:
$this->Tutorsession->recursive = 2;
$this->Tutorsession->Teacher->contain('User');
$this->set('tutor',
$this->Tutorsession->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
)
);
$tutor=$this->Teacher->find('first',
array(
'conditions' => array('Teacher.user_id' => $id)
)
);
debug($tutor); exit();
Your result similar:
'Teacher' => array(
'id' => '1',
'name' => 'abc',
'created' => '1397040385',
'updated' => '1397725860'
),
'User' => array(
'id' => '4',
'name' => 'administrators',
'created' => '1397032953',
'modified' => '1397032953'
),
'Tutorsession' => array(
0=>array(
'id' => '3',
'name'=>'abc',
'created' => '1400729137'
),
1=>array(
'id' => '4',
'name'=>'abc',
'created' => '1400729137'
)
)
Ensure your models are correct

Cannot save associated data with hasMany through (Join Model)

Hi, I am new to cakephp and doing a project on cakephp 2.3.4.
I have to associate product metal class through has many through association . But it doesn't seem to be working.
Model code
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
App::uses('AppModel', 'Model');
class MetalProduct extends AppModel {
public $belongsTo = array(
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id'
),
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id'
)
);}
My database table names are metal, products and metal_products
I have multiple select option for selecting more than one metal type.
This is how I get the the list of metals
$metals=$this->Metal->find('list');
$this->set(compact('metals'));
FormHelper code for listbox is
<?php echo $this->Form->input('Metal',array('type' => 'select',
'multiple' => true)); ?>
The product is getting saved successfully but the associations are not.
The debug array give me this
$message = array(
'Product' => array(
'category_id' => '517a514b-0eb0-4ec9-b018-0b948620d4f0',
'name' => 'mangalsutra Diamond',
'slug' => 'mangalsutra_diamond',
'description' => '1212',
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
'image' => '121212',
'price' => '12121',
'weight' => '12',
'active' => '1',
'category' => 'Mangalsutra'
)
)
I had put my head through walls but no clue why the associations are not getting saved.
The way they say in tutorials it seems easy, but why its not working?
I have doubts that its not saving because the metal array is passed like this
'Metal' => array(
(int) 0 => '5183cb65-bf90-459c-b22e-0b748620d4f0',
(int) 1 => '5183ce25-c744-433e-b035-0b748620d4f0'
),
It should mention 'id''rather than (int) 0 or something.
Also, my database table for metal_products which I have created manually has
id(primary key)
metal_id(foreign key to Metal.id)
product_id(foreign key to Product.id)
Am I doing something wrong with naming conventions or the way database is created?
Please give me correct ans cause anything I tried from others answer is not working
I am saving it via
$this->Product->saveAll($this->request->data, array('deep' => true))
In your model, is all of that in your MetalProduct Model? If so you need to move
class Metal extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Metal model and
class Product extends AppModel {
public $hasMany = array(
'MetalProduct'
);
}
to the Product Model
Also add your belongsTo to each Model
I see you have a join table. Join tables are usually for HABTM associations. This is a HasMany. You need to use the other options available to define the foreign key relationships in each model as outlined above.
Please see the examples on the Cake Documentation.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html
Also, if you are unsure of how to set up the models correctly, you can always use the Bake feature of Cakephp to generate the models and code for you in that regard.
book.cakephp.org/2.0/en/console-and-shells/code-generation-with-bake.html
If you are more of a visual learner this video tut should help you through the basics
http://www.youtube.com/watch?v=kJAMifqF5s8
The Relation i generated using Bake looks something like this
class Product extends AppModel {
/**
* hasAndBelongsToMany associations
*/
public $hasAndBelongsToMany = array(
'Metal' => array(
'className' => 'Metal',
'joinTable' => 'metals_products',
'foreignKey' => 'product_id',
'associationForeignKey' => 'metal_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
class Metal extends AppModel {
/** hasAndBelongsToMany associations */
public $hasAndBelongsToMany = array(
'Product' => array(
'className' => 'Product',
'joinTable' => 'metals_products',
'foreignKey' => 'metal_id',
'associationForeignKey' => 'product_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
));
}
/**
* MetalsProduct Model
* #property Product $Product
* #property Metal $Metal
*/
class MetalsProduct extends AppModel {
/* belongsTo associations */
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Metal' => array(
'className' => 'Metal',
'foreignKey' => 'metal_id',
'conditions' => '',
'fields' => '',
'order' => ''
));
}
This worked for me flawlessly.
Hope it works for all.

Associating an item to multiple other items (of a different class) using Prestashop's backoffice

Having just arrived at Prestashop 1.5, I am making a very simple module: a video of the week, associated with multiple products that need to appear right next to it.
I decided to start from the Backoffice. Right now, I can view, add, edit and remove all the Video entries but I'm a bit lost on how to map the N-N association between a video and its related products... The lack of documentation isn't helping either.
Any ideas how to pull this off?
Here's a bit of my code, the Video class is defined by:
class Video extends ObjectModel {
public $id_video;
public $title;
public $url;
public $active;
public static $definition = array(
'table' => 'video',
'primary' => 'id_video',
'multilang' => false,
'fields' => array(
'id_video' => array(
'type' => ObjectModel :: TYPE_INT
),
'title' => array(
'type' => ObjectModel :: TYPE_STRING,
'required' => true
),
'url' => array(
'type' => ObjectModel :: TYPE_STRING,
'required' => true
),
'active' => array(
'type' => ObjectModel :: TYPE_BOOL,
'required' => true
)
),
);
(...)
and the AdminVideo class is here:
class AdminVideoController extends ModuleAdminController {
public function __construct()
{
$this->table = 'video';
$this->className = 'Video';
$this->lang = false;
$this->fields_list['id_video'] = array(
'title' => $this->l('ID'),
'align' => 'center',
);
$this->fields_list['title'] = array(
'title' => $this->l('Title'),
'width' => 'auto'
);
$this->fields_list['url'] = array(
'title' => $this->l('URL'),
'width' => 'auto'
);
$this->fields_list['active'] = array(
'title' => $this->l('Active'),
'width' => '70',
'align' => 'center',
'active' => 'status',
'type' => 'bool',
'orderby' => false
);
parent::__construct();
}
public function postProcess()
{
parent::postProcess();
}
public function renderList()
{
$this->addRowAction('edit');
$this->addRowAction('delete');
$this->addRowAction('details');
return parent::renderList();
}
public function renderForm()
{
if (!($obj = $this->loadObject(true)))
return;
$this->fields_form = array(
'legend' => array(
'title' => $this->l('This weeks video'),
'image' => '../img/admin/world.gif'
),
'input' => array(
array(
'type' => 'text',
'label' => $this->l('Nome'),
'name' => 'title',
'size' => 33,
'required' => true,
'desc' => $this->l('Title')
),
array(
'type' => 'text',
'label' => $this->l('URL'),
'name' => 'url',
'size' => 33,
'required' => true,
'desc' => $this->l('Video URL')
),
array(
'type' => 'radio',
'label' => $this->l('Active:'),
'name' => 'active',
'required' => false,
'class' => 't',
'is_bool' => true,
'values' => array(
array(
'id' => 'active_on',
'value' => 1,
'label' => $this->l('Enabled')
),
array(
'id' => 'active_off',
'value' => 0,
'label' => $this->l('Disabled')
)
),
'desc' => $this->l('Only one video can be active at any given time')
),
)
);
if (Shop::isFeatureActive())
{
$this->fields_form['input'][] = array(
'type' => 'shop',
'label' => $this->l('Shop association:'),
'name' => 'checkBoxShopAsso',
);
}
$this->fields_form['submit'] = array(
'title' => $this->l(' Save '),
'class' => 'button'
);
if (!($obj = $this->loadObject(true)))
return;
return parent::renderForm();
}
}
One other thing: would it be possible to add a preview of the video inside the backoffice? I tried to echo YouTube's embed code, but it gets inserted even before the header. Is there a clean way of doing this or do I have to use some jQuery trickery? I was basically doing an echo of YT's embed code just before the end of postProcess().
Thanks in advance!
The simplest way to associate the videos to the products is by adding a "products" text field in your "video" table to store a comma separated list of the ids of the associated products (eg.: 1,10,27). Even if it's a bit rudimentary, it should work.
Alternatively, you could use a table like this:
create table video_product (
id_association int not null auto_increment,
id_video int,
id_product int,
primary key (id_association)
);
The problem with this solution is that the PrestaShop ObjectModel core does not provide any method to automatically update or delete the related tables (at least as far as I know), so you have to insert the code to manage the "video_product" table in your "Video" class.
If you want an example of how to do this, you should look at the classes/Product.php script, which manages the product table and all its related tables (categories, tags, features, attachments, etc.).
To have an idea of how the Prestashop database is structured, have a look at the docs/dbmodel.mwb file, which contains the schema of the database; this file can be viewed by using the MySQL Workbench application.

CakePHP - should I use HABTM, and if not, how to setup a join table for multiply self-referencing model?

So I am setting up a course system, and courses have pre-reqs. One course may be a pre-req for many others and also may itself have many pre-reqs. I have a Course model, and I'm currently using a HABTM join table, because I also need to track the type of pre-req (regular, co-req, or what we call "pre-req with concurrency": you can take the pre-req at the same time). Here's the Course model:
class Course extends AppModel {
public $name = 'Course';
public $belongsTo = 'Department';
public $hasMany = array(
'Instance' => array('className' => 'Instance'),
);
public $hasAndBelongsToMany = array(
'Prereq' => array(
'className' => 'Course',
'joinTable' => 'prereq_successor',
'foreignKey' => 'successor_id',
'associationForeignKey' => 'prereq_id',
'unique' => 'keepExisting'
)
);
}
The problem is that I need to construct some data for saveAll (from data uploaded via a file, not a form) and can't figure out how to do it from the docs.
I've tried this (controller code), based on the cakePHP 2.0 book, but it fails silently:
//test based on cakePHP 2.0 book fails:
$data = array(
'Course' => array('id' => 1),
'Prereq' => array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
);
$result = $this->Course->saveAssociated($data);
And I've tried this (controller code) without success:
//test based on cakePHP 2.0 book fails:
$data = array(
array(
'Course' => array('id' => 1),
'Prereq' => array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
)
);
$result = $this->Course->saveAll($data);
Thanks in advance,
Dave
I think the problem is with your data.
Try this:
$data = array(
array(
'Course' => array('id' => 1),
'Prereq' => array(
array(
'successor_id' => 1,
'prereq_id' => 3,
'type' => 'prereq'
)
)
)
);
$result = $this->Course->saveAll($data);

Categories