cakephp save related model without foreign key - cakephp

I would like to create a new model using another parameter
class Beacon extends AppModel {
/**
* Display field
*
* #var string
*/
public $displayField = 'name';
/**
* Validation rules
*
* #var array
*/
public $validate = array(
'UUID' => array(
'uuid' => array(
'rule' => array('uuid'),
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasOne associations
*
* #var array
*/
public $hasOne = array(
'Position' => array(
'className' => 'Position',
'foreignKey' => 'beacon_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
I would like to ping this beacon passing in parameter the beacon UUID not the primary key.
class Ping extends AppModel {
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'Beacon' => array(
'className' => 'Beacon',
'foreignKey' => 'beacon_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
How do I create a Ping object without the beacon primary_key but the UUID ?
on the ping controller I would like to add
public function pingBeaconWithUUID($uuid) {
How do I manage that ?
Thanks !

If the UUID is unique you can use it as value for the id field. The id needs not to be an integer although it is preferable.
But I would also propose to use the approach by AgRizzo and look up the id.

Related

How do a ProductController without database error 42S22

I do an app that show some products with price for register people.
When create the product page show me "Database Error"
"Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Product.utype' in 'where clause'"
My ProductsController is like this:
class ProductsController extends AppController {
public function index() {
$this->Product->recursive = 0;
$products = $this->Product->find('all');
$this->set('products', $Product);
}
/**
* view method
*
* #throws NotFoundException
* #param string $id
* #return void
*/
public function view($id = null) {
$this->Product->id = $id;
if (!$this->Product->exists()) {
throw new NotFoundException(__('Invalid Product'));
}
$this->set('products', $this->Product->read(null, $id));
}
}
And the Model Product.php is like this:
class Product extends AppModel {
public $primaryKey = 'id_prod';
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'Occurrence' => array(
'className' => 'Occurrence',
'foreignKey' => 'occurrence_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Can help me, please?
Thank you.
Per the very explicit error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'Product.utype' in 'where clause'"
Your code is looking for the colum 'utype' in the products table (which apparently doesn't exist). If you're not sure where your code is telling it to do that, just do a project-wide search for "utype" and update it to the correct field name.

how to use countercache for different alias in cakephp model?

I have the following models: Attachment and PurchaseOrder hence the datatables attachments and purchase_orders.
In PurchaseOrder, I have
class PurchaseOrder extends AppModel {
public $hasMany = array(
'Pdf' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Pdf.model' => 'PurchaseOrder'
)
),
'Zip' => array(
'className' => 'Attachment',
'foreignKey' => 'foreign_key',
'conditions' => array(
'Zip.model' => 'PurchaseOrder'
)
),
In Attachment, I have the following:
public $belongsTo = array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array('Pdf.model' => 'PurchaseOrder'),
)
),
My problem is when I try to use $this->PurchaseOrder->Zip->save($data); I run into problem because the alias Pdf is not found.
How do I overcome this while maintaining the countercache behavior of updating the attachment_count inside purchase_orders?
Note that if a PurchaseOrder is associated with 3 Pdf Attachments and 2 Zip Attachments, the attachment_count should read 3.
I am using cakephp 2.4.2
I stopped using counterCache and used afterSave instead.
/**
* Called after each successful save operation.
*
* #param boolean $created True if this save created a new record
* #param array $options Options passed from Model::save().
* #return void
* #link http://book.cakephp.org/2.0/en/models/callback-methods.html#aftersave
* #see Model::save()
*/
public function afterSave($created, $options = array()) {
// update the PurchaseOrder based on pdf
if (isset($this->data['Pdf'])) {
$this->updatePurchaseOrderAttachmentCount($this->data);
}
}
/**
*
* #param Array $data where we expect key Pdf
*/
public function updatePurchaseOrderAttachmentCount($data) {
// check for Pdf
$pdfSet = isset($data['Pdf']);
if (!$pdfSet) {
throw new Exception('we expect Pdf as a key in your $data');
}
// check for foreign_key and model
$pdfFKSet = isset($data['Pdf']['foreign_key']);
$pdfModelSet = isset($data['Pdf']['model']);
if (!$pdfFKSet || !$pdfModelSet) {
throw new Exception('we expect foreign_key and model as keys in your $data["Pdf"]');
}
$count = $this->find('count', array(
'conditions' => array(
'model' => 'PurchaseOrder',
'type' => 'application/pdf',
'foreign_key' => $data['Pdf']['foreign_key'],
)
));
if (!is_numeric($count)) {
throw new Exception('we expect numeric in the $count');
}
$poData = array(
'PurchaseOrder' => array(
'id' => $data['Pdf']['foreign_key'],
'attachment_count' => $count,
)
);
return $this->PurchaseOrder->save($poData);
}
I know this post is very old, but I recently faced a similar issue and found another way to handle the same problem. You can continue using the counterCache approach with multiple counterCache, the only difference would be, you need to add you association on the fly, in the __construct method of your model.
public function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
/**
* When using Aliases, associations need to be added on the fly, otherwise, the CounterScope conditions would result in an SQL Error, during counterCache updates
*/
$this->bindModel(
array('belongsTo' => array(
'PurchaseOrder' => array(
'className' => 'PurchaseOrder',
'foreignKey' => 'foreign_key',
'counterCache' => array(
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder'),
'attachment_count' => array($this->alias . '.model' => 'PurchaseOrder')
)
)
)
)
);
}

Stuck with HasMany through saving and error array_merge()

I'm trying to set up a pretty simple website to create and edit recipes ('Recettes' in french).
I'm an experienced frontend developer with a good knowledge (not advanced) of php, and I have been working on CakePhp with a team of developers in the past. I'm kind of learning the bases of starting a project from scratch and it is really not that easy.
I've set up a project, database (Ingredients, Recette and IngredientRecette) and used Bake to generate most of the code.
I have a working CRUD workflow for my 3 models and the only thing left to do is to be able to add ingredients from the Recette/add page but i'm stuck. I followed the instructions on the cake php website (http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasmany-through-the-join-model) and other website (cause the help on cakephp's doc is really not that helpful) and I beleive I have setup a correct Hasmany Through relationship.
But the saveAll function doesn't save anything in the join model (IngredientRecette) and I've came accross a particular error (see below) after I've successfully solved another one that took me a couple of hours to work out !
So I have the feeling that I've checked, and double checked, and triple checked everything in my code and I've read every questions and answers on forums etc...
And I am stuck with this problem, maybe there is something obvious I didn't figured, or maybe and definitely don't understand how Cakephp really wordk :(
Anyway, thanks in advance for those who will be able to bring there advices or help on this :
Here is the error I get after I submitted /recettes/add (I added some form elements to add an ingredient and a quantity to my Recette) :
Warning (2): array_merge() [function.array-merge]: Argument #2 is not an array [CORE\Cake\Model\Model.php, line 2250]
Here is the array I'm passing to the saveAll() method in the controller (output from debug()):
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
)
)
Here is my controller code :
<?php
App::uses('AppController', 'Controller');
/**
* Recettes Controller
*
* #property Recette $Recette
*/
class RecettesController extends AppController {
/**
* index method
*
* #return void
*/
public function index() {
$this->Recette->recursive = 0;
$this->set('recettes', $this->paginate());
}
/**
* view method
*
* #param string $id
* #return void
*/
public function view($id = null) {
$this->Recette->id = $id;
if (!$this->Recette->exists()) {
throw new NotFoundException(__('Invalid recette'));
}
$this->set('recette', $this->Recette->read(null, $id));
}
/**
* add method
*
* #return void
*/
public function add() {
if ($this->request->is('post')) {
debug($this->request->data);
$this->Recette->create();
if ($this->Recette->saveAll($this->request->data)) {
$this->Session->setFlash(__('The recette has been saved'));
//$this->redirect(array('action' => 'index'));
} else {
$this->Session->setFlash(__('The recette could not be saved. Please, try again.'));
}
}
$this->loadModel('Ingredient');
$liste_ingr = $this->Ingredient->find('all');
$this->set('liste_ingr', $liste_ingr);
}
The IngredientRecette model
<?php
App::uses('AppModel', 'Model');
/**
* Recette Model
*
* #property IngredientRecette $ingredient
*/
class Recette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_recette';
/**
* Display field
*
* #var string
*/
public $displayField = 'nom';
public $recursive = 2;
/**
* Validation rules
*
* #var array
*/
public $validate = array(
'id_recette' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'auteur' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nom' => array(
'notempty' => array(
'rule' => array('notempty'),
),
),
'niveau' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
'nb_personnes' => array(
'numeric' => array(
'rule' => array('numeric'),
),
),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'AssociatedIngredient' => array(
'className' => 'IngredientRecette',
'foreignKey' => 'id_recette',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);
}
And IngredientRecette model (the join model)
<?php
App::uses('AppModel', 'Model');
/**
* IngredientRecette Model
*
* #property ingredient $ingredient
* #property recette $recette
*/
class IngredientRecette extends AppModel {
/**
* Use database config
*
* #var string
*/
public $useDbConfig = 'default';
/**
* Use table
*
* #var mixed False or table name
*/
public $useTable = 'ingredient_recette';
/**
* Primary key field
*
* #var string
*/
public $primaryKey = 'id_ingredient_recette';
public $recursive = 2;
//The Associations below have been created with all possible keys, those that are not needed can be removed
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'IngredientLiaison' => array(
'className' => 'Ingredient',
'foreignKey' => 'id_ingredient',
'conditions' => '',
'fields' => '',
'order' => ''
),
'RecetteLiaison' => array(
'className' => 'Recette',
'foreignKey' => 'id_recette',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
So nothing is saved in the join model and I have this warning...
Any help appreciated and of course please let me know if anything is unclear !
thanks a lot !!
I think your data is not formatted correctly. How about trying this instead (notice the extra array I put inside Associated Ingredient). Since recipe hasMany associated ingredient, the data for Associated Ingredient should be a series of arrays.
array(
'Recette' => array(
'auteur' => '7',
'nom' => 'Nouvelle Recette',
'cout' => 'Pas cher',
'niveau' => '5',
'tps_realisation' => '30 min',
'nb_personnes' => '6',
'description' => 'Description data',
'remarque' => ''
),
'AssociatedIngredient' => array(
array(
'id_ingredient' => '6',
'quantite' => '70 cl',
'id_recette' => '7654'
),
array( /*ingredient 2 data*/ ),
)
);

CakePHP Many To Many With Conditions

I'm newbie on CakePHP, and now I'm stuck on many to many situation
ok, i have 3 Table :
questions
with fields (id, question)
question_product
with fields (id, question_id, product_id, question_number, is_enabled)
products
with fields (id, name, code, is_enabled)
so when i want to select questions with specific field, i don't know how to fix it
for now, my code is like this :
Question.php (Model)
class Question extends AppModel {
public $hasAndBelongsToMany = array (
'Product' => array (
'joinTable' => 'question_product',
'foreignKey' => 'question_id',
'associationForeignKey' => 'product_id',
'unique' => 'keepExisting',
'order' => 'question_number',
'fields' => array (
'QuestionProduct.question_number',
'Product.id',
'Product.name'
),
'conditions' => array (
'QuestionProduct.is_enabled' => 1,
)
)
);
}
QuestionsController.php (Controller)
public function loadQuestions($productId) {
$this->view = 'load_questions';
$questions = $this->Question->find('all', array (
'fields' => array (
'Question.id',
'Question.question',
'Question.is_optional',
'Question.reason_optional',
'Question.text_size'
),
'conditions' => array (
'QuestionProduct.product_id' => $productId
)
));
$this->set($questions);
}
method loadQuestions have one parameter to select with specified product
if i using sql query, it will be like this
select all from Question with condition Product.product_id=4, sorted by QuestionProduct.question_number ascending
select questions.*
from questions
join question_product on questions.id=question_product.question_id
join products on products.id=question_product.product_id
where products.id=4
order by question_product.question_number;
any answer will be appreciated :)
Thanks !
Any time you use a many-many (HABTM) relation with any other field that requires conditions, it is no longer many-many as far as Cake is concerned. You want the HasManyThrough relationship
Instead of using hasAndBelongsToMany relation, use two belongsTO relation from question_product to questions and another time from question_product to products.
question_product belognsTo questions
question_product belongsTo products
NOTE:you should change the table name from question_product to question_products as cakePHP convention
in your model QuestionProduct model :
<?php
// declares a package for a class
App::uses('AppModel', 'Model');
class QuestionProduct extends AppModel {
/**
* #see Model::$actsAs
*/
public $actsAs = array(
'Containable',
);
/**
* #see Model::$belongsTo
*/
public $belongsTo = array(
'Product' => array(
'className' => 'Product',
'foreignKey' => 'product_id',
),
'Question' => array(
'className' => 'Question',
'foreignKey' => 'question_id',
),
);
then in your Controller :
public function loadQuestions($productId) {
$this->view = 'load_questions';
$questions = $this->QuestionProduct->find('all', array (
'fields' => array (
'Question.id',
'Question.question',
'Question.is_optional',
'Question.reason_optional',
'Question.text_size'
),
'conditions' => array (
'QuestionProduct.product_id' => $productId
),
'contain' => array('Product','Question')
));
$this->set($questions);
}
It should make exactly the query you want, and I don't think it has any other way to produce that query.

Associations in CakePHP with non-conventional database

We are converting an application for use with CakePHP 2.0.3.
For some reason, I cannot seem to set proper relations between my models.
Here's an example:
User (id, petid, country, picid, ...)
Pet (id, userid, picid, ...)
Picture (id, albumid, ....)
Album (id, userid, petid, ...)
The meanings of these are the following:
- A user can have multiple pets, but can only have selected one pet at the same time (therefore, petid in User)
- Pets belong to one user
- Pets and Users can have multiple pictures, but only one profile picture, therefore Pet.picid and User.picid
- Pets and users can have multiple Albums
I set up my models in CakePHP, but I cannot figure out which relations to use between them since the Database is not following the conventions.
I've tried the following:
User
-> hasMany(Pets)
-> hasOne(Picture)
-> hasMany(Album)
Pet
-> belongsTo(User) (works fine, with foreignkey userid)
-> hasMany(Album)
-> hasOne(Picture)
Album
-> hasMany(Picture)
---- Logic to achieve this? It either belongs to a user or pet-----
-> belongsTo(User)
-> belongsTo(Pet)
Picture
-> belongsTo(Album)
I'm new to CakePHP and cannot figure out the way to go here.
Do you have any suggestions?
I would suggest using Aliases in your relationships which will help get your head around the data being returned.
For example, your User model could use SelectedPet and ProfilePicture in it's associations:
User.php model
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'SelectedPet' => array(
'className' => 'Pet',
'foreignKey' => 'petid'
),
'ProfilePicture' => array(
'className' => 'Picture',
'foreignKey' => 'picid',
)
);
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'Album' => array(
'className' => 'Album',
'foreignKey' => 'userid',
'dependent' => false
),
'Pet' => array(
'className' => 'Pet',
'foreignKey' => 'userid',
'dependent' => false
)
);
Your Pet model could use ProfilePicture as well:
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'userid'
),
'ProfilePicture' => array(
'className' => 'Picture',
'foreignKey' => 'picid'
)
);
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'Album' => array(
'className' => 'Album',
'foreignKey' => 'petid',
'dependent' => false
)
);
Picture model:
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'Album' => array(
'className' => 'Album',
'foreignKey' => 'albumid'
)
);
..and finally your Album model:
/**
* belongsTo associations
*
* #var array
*/
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'userid'
),
'Pet' => array(
'className' => 'Pet',
'foreignKey' => 'petid'
)
);
/**
* hasMany associations
*
* #var array
*/
public $hasMany = array(
'Picture' => array(
'className' => 'Picture',
'foreignKey' => 'albumid',
'dependent' => false
)
);
With regards to the logic of an Album belonging to a User or a Pet, you could just handle this in your controller when saving data or returning it. I.e User is given preference over Pet.
I hope this helps.

Resources