Problem with CakePHP using autoModel when it shouldn't - php

I am getting an error in my view:
Warning (512): SQL Error: 1054: Unknown column 'Model.id' in 'where clause' [CORE\cake\libs\model\datasources\dbo_source.php, line 525]
$sql = "SELECT Model.model_id, Model.pic_location, Model.model_name FROM models AS Model WHERE Model.id = '20' LIMIT 1"
$error = "1054: Unknown column 'Model.id' in 'where clause'"
$out = null
My model is unfortunately named "Model", due to the legacy database I am working with. I have used debug and determined that it is using primaryKey of 'id' (as also displayed in the SQL above), despite my model code (see below) explicitly setting the primaryKey to 'model_id'.
The database table is models with a primary key of model_id, so I can only assume the system is using an autoModel.
My model code is 'model.php':
<?php class Model extends AppModel {
var $name = 'Model';
var $primaryKey = 'model_id';
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $hasMany = array(
'Pic' => array(
'className' => 'Pic',
'foreignKey' => 'model_name',
'dependent' => false,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'exclusive' => '',
'finderQuery' => '',
'counterQuery' => ''
)
);}?>
I originally created the by hand, and when I hit this error, I baked it... with identical results (except I didn't include the empty fields in the hasMany relationship).
I have turned application level caching off temporarily, and cleared the cache, but nothing helps. How can I make it use my model code?

I'm afraid you'll have to rename your Model model to something else. Your Model class extends AppModel, which in turn extends the base model class, which is also called Model. You're bound to get into trouble this way.
If you have a legacy database, just set the $useTable attribute to use a table with a name different from your model.

Related

Cakephp 2.x - Containable refusing to work

I have the following Model structure:
Models:
Opening
Application
Applicant
Group
The first three was created on initial setup of the application, with Groups added via the bake script afterwards.
Model associations:
Applicant hasMany Application
Application hasMany Applicant
Application belongsTO Opening
Opening hasMany Application
Opening belongsTo Group
Group hasMany Opening (added after bake script ran)
I am trying to retrieve data in the Application model but I'm having some issues. I can't seem to get to the Group table. I keep getting told the column I'm looking for doesn't exist. My query:
$options = array(
'fields' => array(
'Application.applicant_id',
'Application.opening_id',
'Application.value_1',
'Application.date',
'Applicant.first_names',
'Applicant.surname',
'Applicant.gender',
'Applicant.id_number',
'Opening.title',
'Group.group_xs',
'Group.group_name'
),
'recursive' => -1,
'contain' => array(
'Applicant',
'Opening.Group',
),
'conditions' => array(
'Applicant.id' => $applicant_id,
'Opening.id' => $opening_id
)
);
$result = $this->find('all', $options);
Error message:
Column not found: 1054 Unknown column 'Group.group_xs' in 'field list'
I have tried so many different variations of that, it's not even funny. I've tried both with and without 'recursive', with 'recursive' => -1, 0, and 1. I also tried the below variations on the 'contain' key, among many others.
'contain' => array(
'Applicant',
'Opening' =>array('Group')
),
'contain' => array(
'Applicant',
'Opening',
'Group'
),
'contain' => array(
'Group'
),
I load the Containable behavior at the top of my Application model as follows:
public $actsAs = array('Containable' => array('recursive' => true,'autofields' => true));
Can anyone please help. What is going on here?
I think that the containable only works on "has"-relations and not on "belongsTo"-relations.
Try using joining tables https://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables

CakeDC search plugin using complex conditions in query with HABTM

I wrote 2 days ago to ask about andConditions and it appeared that I didn't understand the idea but the fact is that for two days now I am stuck with the next step using CakeDC:
How do I implement complex HABTM conditions in "query" methods for CakeDC search plugin?
I have Offer HABTM Feature (tables: offers, features, features_offers) and the below works just fine when used in controller:
debug($this->Offer->find('all', array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id in (8, 10)',
)
)
)
)
)
);
The problem comes when I want to use the same conditions in the search:
public $filterArgs = array(
array('name' => 'feature_id', 'type' => 'query', 'method' => 'findByFeatures'),
);
........
public function findByFeatures($data = array()) {
$conditions = '';
$featureID = $data['feature_id'];
if (isset($data['feature_id'])) {
$conditions = array('contain' => array(
'Feature' => array(
'conditions' => array(
'Feature.id' => $data['feature_id'],
)
)
)
);
}
return $conditions;
}
I get an error:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column
'contain' in 'where clause'
which makes me think that I cannot perform this search and/or use containable behavior in searches at all.
Can someone with more experience in the field please let me know if I am missing something or point me to where exactly to find a solution for that - perhaps a section in the cookbook?
EDIT: Also tried the joins. This works perfectly fine in the controller, returning all the data I need:
$options['joins'] = array(
array('table' => 'features_offers',
'alias' => 'FeaturesOffers',
'type' => 'inner',
'conditions' => array(
'Offer.id = FeaturesOffers.offer_id'
),
array('table' => 'features',
'alias' => 'F',
'type' => 'inner',
'conditions' => array(
'F.id = FeaturesOffers.feature_id'
),
)
),
);
$options['conditions'] = array(
'feature_id in (13)' //. $data['feature_id']
);
debug($this->Offer->find('all', $options));
... and when I try to put in the search method I get the returned conditions only in the where clause of the SQL
WHERE ((joins = (Array)) AND (conditions = ('feature_id in Array')))
...resulting in error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'joins' in 'where clause'
EDIT: Maybe I am stupid and sorry to say that but the documentation of the plugin sucks a ton.
I double, triple and quadruple checked (btw, have lost already 30 hours at least on 1 filed of the search form facepalm) and the stupid findByTags from the documentation still doesn't make any sense to me.
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
As I understand it
$this->Tagged
is supposed to be the name of the model of the HABTM association.
This is quite far from the standards of cakePHP though: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm
The way it is described here, says that you don't need another model but rather you associate Recipe with Ingredient as shown below:
class Recipe extends AppModel {
public $hasAndBelongsToMany = array(
'Ingredient' =>
array(
'className' => 'Ingredient',
'joinTable' => 'ingredients_recipes',
'foreignKey' => 'recipe_id',
'associationForeignKey' => 'ingredient_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
meaning that you can access the HABTM assoc table data from Recipe without needing to define model "IngredientRecipe".
And according to cakeDC documentation the model you need is IngredientRecipe and that is not indicated as something obligatory in the cakePHP documentation. Even if this model is created the HABTM assoc doesn't work properly with it - I tried this as well.
And now I need to re-write the search functionality in my way, using only cakePHP even though I spent already 30 hours on it... so unhappy. :(
Every time I come to do this in a project I always spend hours figuring out how to do it using CakeDC search behavior so I wrote this to try and remind myself with simple language what I need to do. I've also noticed that although Using the CakeDC search plugin with associated models this is generally correct there is no explanation which makes it more difficult to modify it to one's own project.
When you have a "has and belongs to many" relationship and you are wanting to search the joining table i.e. the table that has the two fields in it that joins the tables on either side of it together in a many-to-many relationship you want to create a subquery with a list of IDs from one of the tables in the relationship. The IDs from the table on the other side of the relationship are going to be checked to see if they are in that record and if they are then the record in the main table is going to be selected.
In this following example
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
all the records from ArosHandover will be selected if they have an aro_id of 3 then the Handover.id is used to decide which Handover records to select.
On to how to do this with the CakeDC search behaviour.
Firstly, place the field into the search form:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
etc...
notice that I have not placed the form element in the ArosHandover data space; another way of saying this is that when the form request is sent the field aro_id will be placed under the array called Handover.
In the model under the variable $filterArgs:
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
notice that the type is 'subquery' as I mentioned above you need to create a subquery in order to be able to find the appropriate records and by setting the type to subquery you are telling CakeDC to create a subquery snippet of SQL. The method is the function name that are going to write the code under. The field element is the name of the field which is going to appear in this part of the example query above
WHERE Handover.id in
Then you write the function that will return the subquery:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
In the Handovers controller:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
If you want any further explanation as to why I have done something, ask and if I get an email to tell me that you have asked I will give an answer if I am able to.
This is not an issue of the plugin but how you build the associations. You need to properly join them for a search across these three tables. Check how CakePHP is fetching the data from HABTM assocs by default.
http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#joining-tables
Suppose a Book hasAndBelongsToMany Tag association. This relation uses
a books_tags table as join table, so you need to join the books table
to the books_tags table, and this with the tags table:
$options['joins'] = array(
array('table' => 'books_tags',
'alias' => 'BooksTag',
'type' => 'inner',
'conditions' => array(
'Books.id = BooksTag.books_id'
)
),
array('table' => 'tags',
'alias' => 'Tag',
'type' => 'inner',
'conditions' => array(
'BooksTag.tag_id = Tag.id'
)
)
);
$options['conditions'] = array(
'Tag.tag' => 'Novel'
);
$books = $Book->find('all', $options); Using joins allows you to have
a maximum flexibility in how CakePHP handles associations and fetch
the data, however in most cases you can use other tools to achieve the
same results such as correctly defining associations, binding models
on the fly and using the Containable behavior. This feature should be
used with care because it could lead, in a few cases, into bad formed
SQL queries if combined with any of the former techniques described
for associating models.
Also your code is wrong somewhere.
Column not found: 1054 Unknown column 'contain' in 'where clause'
This means that $Model->contain() is somehow called. I don't see such a call in your code pasted here so it must be somewhere else. If a model method can not be found this error usually happens with the field name as column.
I want to share with everyone that the solution to working with HABTM searches with the plugin lies here: Using the CakeDC search plugin with associated models
#burzum, the documentation is far from ok man. Do you notice the use of 'type' => 'checkbox' and that it is not mentioned anywhere that it is a type?
Not to mention the total lack of grammar and the lots of typos and missing prepositions. I lost 2 days only to get a grasp of what the author had in mind and bind the words in there. No comment on that.
I am glad that after 5 days on the uphill work I made it. Thanks anyway for being helpful.

Cake PHP paginate multiple tables of one model

community,
there is a problem with multiple tables which receive their entries of the same model "Post" on the same page. When clicking a paginator-number the app will change the page for both tables.
I tried it with dummy classes in the AppModel which should work but I get an error saying an internal error has occurred (error 500). I found out that there is a problem with the corresponding SQL-statement with a not found row.
In the AppModel:
class PostHood extends Post {
public $useTable = 'posts';
};
That code uses the table "cake_posts" of "class Post extends AppModel". In the controller I tried to get the PostHood like following:
$this->paginate['PostHood'] = array(
'conditions' => array('OR' => array(stuff)),
'limit' => 5
);
$this->set('postsHood', $this->paginate('PostHood'));
In the view there is a foreach-loop using the $postsHood as $post.
Maybe you have an idea, thanks in advance :)
EDIT 1:
I got some error notices after changing the code. May be you have an idea what to do.
Change of the AppModel:
class PostHood extends AppModel {
var $name = 'Post';
public $useTable = 'posts';
};
The Controller:
$this->loadModel('PostHood');
$this->paginate = array(
'conditions' => array('OR' =>
array(
array('AND' => array(
array('PostHood.ZIPCODE LIKE' => $userArea . '%'),
array('PostHood.ALTDATE >' => date("Y-m-d")),
array('PostHood.AGENT' => '0'),
array('PostHood.OWNER <>' => $this->UserAuth->getUserId()),
array('PostHood.PARENTID' => '0'),
array('PostHood.ACCEPTED' => '0')
)), [MORE AND ARRAYS]
$this->set('postsHood', $this->paginate('PostHood'));
The corresponding error in the view:
Error: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'Post.DATE' in 'order clause'
SQL Query: SELECT PostHood.id, PostHood.B/S, PostHood.H, PostHood.CITY,
PostHood.MARKET, PostHood.DATE, PostHood.ALTDATE, PostHood.TIME, PostHood.INCOME, PostHood.ZIPCODE, PostHood.ALIAS, PostHood.VEHICLE, PostHood.DELIVERYAREA, PostHood.SPACE, PostHood.STREET, PostHood.HOUSENUMBER, PostHood.NAME, PostHood.CART, PostHood.TEL, PostHood.OWNER, PostHood.created, PostHood.modified, PostHood.AGENT, PostHood.EXTRADATA, PostHood.PARENTID, PostHood.BRATED, PostHood.SRATED, PostHood.REQUESTED, PostHood.ACCEPTED FROM usr_web126986_4.cake_posts AS PostHood WHERE 1 = 1 ORDER BY Post.DATE asc LIMIT 5
Obviously Cake tries to fetch data with "PostHood" but the table "posts" which I actually want to use is listening to "Post.field". How can I fix that? Thanks :)
You should try something like this:
$this->paginate = array(
'conditions' => array('OR' => array(stuff)),
'limit' => 5
);
$this->set('postsHood', $this->paginate('PostHood'));
I am just curious as to why you want to structure your model extending another model.
Got it working with the great plugin DataTables. That overrides the cake-pagination and uses JSON to display the results.
In order to implement it into cake you need
1.) a cake component for interpreting sql-statements: https://github.com/cnizzdotcom/cakephp-datatable
2.) the plugin: http://www.datatables.net/index
3.) implementation:
$this->paginate = array(
'fields' => array('Model.field1', 'Model.field2'),
'conditions' => array( stuff )
);
$response = $this->DataTable->getResponse();
$this->set('response', $response);
$this->set('_serialize','response');
$encode = json_encode($response);
$this->set('dataTablesData', $encode);
Then you can grab the data in the view.

cakephp SQL error at $this->User->saveField()

When I try to update a user, let's say his account balance
$this->User->id = $validUserId;
$this->User->saveField('balance', 100);
I get this error
SQL Error: 1054: Unknown column 'User.group_id' in 'field list'
with this automatically generated query druring the save process
SELECT `User`.`group_id` FROM `users` AS `User` WHERE `User`.`id` = *validUserId* LIMIT 1
The user belongsTo a UserGroup and therefore the user has user_group_id attribute, is there any way to tell cake, that the attribute is related to a UserGroup?
Thanks in advance,
EL
Maybe, if you are just saving a field, you don't need to do validations. In that case just do
$this->User->saveField('balance', 100, false);
http://api.cakephp.org/class/model#method-ModelsaveField
Or if there is a callback like beforeSave or something, I think you can do something like:
$this->save($data, array('validate'=>false, 'callbacks'=>false));
Hope this helps
Most certainly, the models should look like this:
class UserGroup extends AppModel {
var $belongsTo = array(
'UserGroup' => array(
'className' => 'UserGroup',
'foreignKey' => 'user_group_id'
)
);
}
class User extends AppModel {
var $hasMany = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_group_id'
)
);
}
Read about associating CakePHP models in the book

Mysql : Join/Relating two tables

I have two table
1. Airline -id(primary), name
2. Form - id(primary), operator, other unwanted fields
I want to relate Airline.name to Form.operator. Is it possible since Form.operator is not primary key, if yes give me the query.
Can some one also guide me as how will the cakephp model relation be in this case
I would advise you to not use the name Form as is it used elsewhere in the system, however try this (or something similar) and read http://book.cakephp.org/view/1039/Associations-Linking-Models-Together
In app/models/airline.php:
<?php
class Airline extends AppModel
{
var $name = 'Airline';
var $hasOne = array(
'Form' => array(
'className' => 'Form',
'foreignKey' => 'operator')
);
// other stuff
// ... //
?>
In app/models/form.php:
<?php
class Form extends AppModel
{
var $name = 'Form';
var $belongsTo = array(
'Airline' => array(
'className' => 'Airline',
'foreignKey' => 'operator')
)
;
// other stuff
// ... //
?>
var $hasOne = array(
'airline' => array(
'className' => 'airline',
'foreignKey' => false,
'conditions' => array(
'`form`.`yourfield` = `airline`.`yourfield`'
)
)
}
This should work. just replace your fields
in order to make the relations, as Leo suggested, work, you have to follow the cake conventions. In order to save you some headaches later on, I would therefore suggest the nicely written and short material here and here. You will learn e.g. that a good foreign key for which cakephp can do some lifting for you is named operator_id, instead of simply operator (if operator is not yet a foreign key, it could be that you have a database design issue).
Lifting here refers to automatically recognizing relations once defined in e.g. a $belongsTo.
select * from `airline`, `form` where `airline.id`=`form.operator`

Categories