I a making a plugin in CakePhp and I am using multiple models inside the plugin. I created those models by using the console and baked them. I ran into a problem when using 1 of them and it only appears with this specific model.
The model file name:
GamificationBadgeUnlockActionProgression.php
Content of model:
<?php
App::uses('GamificationAppModel', 'Gamification.Model');
class GamificationBadgeUnlockActionProgression extends GamificationAppModel {
function __construct() {
parent::__construct();
echo 'oleej';
exit;
}
public $belongsTo = array(
'GamificationBadgeProgression' => array(
'className' => 'GamificationBadgeProgression',
'foreignKey' => 'gamification_badge_progression_id'
),
'GamificationBadgeUnlockAction' => array(
'className' => 'GamificationBadgeUnlockAction',
'foreignKey' => 'gamification_badge_unlock_action_id'
)
);
public function addMissingProgression($missingProgression)
{
/*$this->saveMany($missingProgression, array(
'fieldlist' => array('gamification_badge_progression_id', 'gamification_badge_unlock_action_id')
));*/
}
}
In the controller of the plugin I have:
public $uses = array('Gamification.GamificationBadge',
'Gamification.GamificationBadgeProgression',
'Gamification.GamificationBadgeUnlockAction',
'Gamification.GamificationBadgeUnlockActionProgression',
'Gamification.GamificationBadgeUnlockType');
All the other models are being loaded, except 'Gamification.GamificationBadgeUnlockActionProgression'. Which is very weird. It has all required stuff and it follows the naming conventions and everything.
It just isn't being loaded as the constructor is never called and the echo is never shown.
When I call the function from the model like this with an array as parameter:
$this->GamificationBadgeUnlockActionProgression->addMissingProgression($missingProgression);
Then I get an error
SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'addMissingProgression' at line 1
Because the model is not loaded.
However CakePhp does create a magic model with the same name, which results in the error above, because the model magically excists but the function ofcourse doesnt and neither do the relations.
What am I missing here?
That kind of error is thrown when the Model isn't properly loaded, it tries to execute the method as an SQL function, which obvisouly doesn't exist. At the top of your Controller, check if it has:
App::uses('GamificationBadgeUnlockActionProgression', 'Gamification.Model');
That, combined with the Gamification.GamificationBadgeUnlockActionProgression in the $uses array that you already have should be enough for it to load. If it still fails, as a last resort you could manually load it in your method using a fallback like:
if (!is_object($this->GamificationBadgeUnlockActionProgression)) {
ClassRegistry::init('Gamification.GamificationBadgeUnlockActionProgression');
$this->GamificationBadgeUnlockActionProgression = new GamificationBadgeUnlockActionProgression();
}
That would manually load the Model and create an instance for it under $this->GamificationBadgeUnlockActionProgression.
There also appears to be an error in the associations in the model, it should include the Plugin prefixes, like:
public $belongsTo = array(
'GamificationBadgeProgression' => array(
'className' => 'Gamification.GamificationBadgeProgression',
'foreignKey' => 'gamification_badge_progression_id'
),
'GamificationBadgeUnlockAction' => array(
'className' => 'Gamification.GamificationBadgeUnlockAction',
'foreignKey' => 'gamification_badge_unlock_action_id'
)
);
Related
Okay, so I have a form in CakePHP that submits to a database. In this form it submits a field called client_id and it is stored in the database as such as well.
Then I have a view to allow me to view all of the invoices that have ever been created. To view the client responsible for the invoice, I can currently only see the id entered in the form by placing: <?php echo $invoice['Invoice']['client_id']; ?> in the view.
The invoices go to one database called: invoices
The clients name is not stored in the invoices table, just the id
The clients information is stored in one database called: clients
I want to be able to actually display out the clients real name in the invoices view rather than the client id.
I tried adding the following query to my index controller, But i'm not sure what to do after this or if this is even right.
$this->set('clients', $this->Invoice->query("SELECT * FROM clients WHERE id='89545'"));
In order to keep this question short in the first place, Please request specific code by commenting. i.e. Controller code, view code, etc... Thank you in advance.
Additional Thoughts
If I wasn't using CakePHP I could use something like the following, So I guess I just don't know how to put this into cakephp "language".
<?php
query($con, "SELECT name FROM clients WHERE id='$client_id'");
echo $row['name'];
?>
roughly!
Update
Here are my models
First one being the Client.php
<?php
class Client extends AppModel {
public $hasMany = array(
'Invoice' => array(
'className' => 'Invoice',
'foreignKey' => 'client_id'
)
);
}
?>
Second one being the Invoice.php
<?php
class Invoice extends AppModel {
public $belongsTo = array(
'Client' => array(
'className' => 'Client',
'foreignKey' => 'client_id'
)
);
}
?>
And finally, to get my invoices from the database, I am using the following inside: InvoicesController.php
public function index() {
$this->set('invoices', $this->Invoice->find('all'));
}
Have you properly set up your model associations? If so you should be able to do something like $invoice['Client']['client_name'] assuming you didn't use recursive = -1.
/edit: Ok I don't mind throwing some code out, but this is a fundamental concept you'll have to try to wrap your head around. Every model that is connected to another model has to have the association set up or things will be painful.
I am assuming a Client hasMany Invoice. So each invoice is specific to a client, and they can have multiple invoices (ex. October 2013 vs November 2013 invoice). From the CakePHP page we see:
class User extends AppModel {
public $hasMany = array(
'Comment' => array(
'className' => 'Comment',
'foreignKey' => 'user_id',
'conditions' => array('Comment.status' => '1'),
'order' => 'Comment.created DESC',
'limit' => '5',
'dependent' => true
)
);
}
So using that as our template, we end up with:
public $hasMany = array(
'Invoice' => array(
'className' => 'Invoice',
'foreignKey' => 'client_id'
)
);
And that goes in Client.php. As the inverse of hasMany is belongsTo, we have an Invoice belongsTo Client. Again, using the CakePHP page as the template, we end up with:
public $belongsTo = array(
'Client' => array(
'className' => 'Client',
'foreignKey' => 'client_id'
)
);
And that goes in Invoice.php. Once you set up those associations, whenever you do something like $this->Invoice->find('all'); or $this->paginate('Invoice');, with proper $recursive settings, Cake will grab the corresponding Client record. This allows you to do what I said before, something like $invoice['Client']['client_name'].
You can also use the $actsAs = array('Containable'); in the model in order to use the 'contain' directive in your controller's find method (instead of using recursive which most likely will get unwanted data on models with many relations)
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.
I'm using php.activerecord, and I am trying to link tables together. I'm not using their structure, but php.activerecord assumes I am, so it doesn't always work. I'm trying to use it on an already made app, so I can't change the database.
I learned from my previous question - Model association with custom table and key names - that I need to be as explicit as possible with the primary_key and foreign_key fields.
I'm having issues now using has_many through. I keep getting NULL, and I have no idea why.
So, here's a scenario: I have 3 tables, contacts, contactPrefs, and preferences. Those tables are as follows
contacts
--------
contactID
name
status
contactPrefs
------------
contactID
prefID
prefValue
preferences
-----------
prefID
name
description
Each contact has multiple contactPrefs. Each contactPrefs has one preferences. I tried to use has_many to get this working, but it's not. Here are my models:
Contacts.php:
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
}
ContactPref.php:
<?php
class ContactPref extends ActiveRecord\Model {
static $table_name = 'contactPrefs';
static $belongs_to = array(
array(
'contact',
'foreign_key' => 'contactid',
'primary_key' => 'contactid'
),
array(
'preference',
'foreign_key' => 'prefid',
'primary_key' => 'prefid'
)
);
}
Preference.php:
<?php
class Preference extends ActiveRecord\Model {
static $primary_key = 'prefID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'class_name' => 'ContactPref'
)
);
}
According to the docs, I now should be able to the following:
<?php
var_dump(Contact::find(1234)->preference);
I cannot. I get NULL. Oddly, I can do this:
<?php
var_dump(Contact::find(1234)->prefs[0]->preference);
That works correctly. But, shouldn't I be able to access the preference object directly through the contact object? Am I misunderstanding the docs (they aren't the greatest, in my opinion)? Am I doing something wrong?
First you are reading the docs with a small flaw. In the docs you are shown:
$order = Order::first();
# direct access to users
print_r($order->users); # will print an array of User object
Which you are already doing via Contact::find(1234)->prefs. Let me boil it down a bit
$contact = Contact::find(1234);
# direct access to prefs
print_r($contact->prefs); # will print an array of ContactPref object
Second, what you actually want is undefined. What should Contact::find(1234)->preference actually do? Return the preference of the first ContactPref? Return an array of Preference objects?
I feel like offering both:
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
public function get_preference() {
return isset($this->prefs[0])
? $this->prefs[0]->preference
: null
;
}
public function get_preferences() {
$preference=array();
foreach($this->prefs as $pref) {
$preference[]=$pref;
}
return $preference;
}
}
Let me explain a little bit what I have done. The ActiveRecord\Model class has a __get($name) function that looks for another function called get_$name, where $name in your case is preference (for the first result) and preference (for the entire collection). This means you can do Contact::find(1234)->preference which would be the same as doing Contact::find(1234)->prefs[0]->preference (but safer, due to the check) and Contact::find(1234)->preferences to get the entire collection of preferences.
This can be made better or optimized in numerous ways, so please don't take it as it is, but do try and adapt it to your specific situation.
For example you can either use the id of the preference as an index in the array or either not force a load of more data from ContactPrefs than the ones you are going to use and try a more intricate query to get the preference objects that you specifically need.
If I find a better implementation by getting through to work in the relationship definition, I'll return. But seeing the Unit Tests for active record, I'm skeptical.
There are several things that look strange, so it's not easy to come to a "this will fix it" for you, but this is an issue at least:
Fieldnames should always be lower-case in phpactiverecord. SQL doesn't mind it either way (not that table names ARE case-sensitive, but column names aren't). So make this:
static $primary_key = 'contactID';
into
static $primary_key = 'contactid';
The connections // find commands can be used in SQL, in which case it doesn't really matter how your key-string is 'cased', so some stuff works. But if the connection goes trough the inner-workings of phpmyadmin, it will fail. So check out this contactID but also the prefID.
Again, this goes only for COLUMN names, so don't go changing classnames or table-names to lowercase.
(extra point: phpmyadmin has trouble with combined primary keys. So while it might be ugly, you could add an extra row to your contactprefs table (if you don't allready have it) called id, to make that table actually have something to work with. It wouldn't give you much trouble, and it would help the activerecord library a lot)
Try the following:
<?php
var_dump(Contact::find(1234)->preferences);
The documentation says that with a has_many relationship, it should be referenced by a plural (http://www.phpactiverecord.org/projects/main/wiki/Associations#has_many_through). The Contact::find(1234) returns a Contact object which has multiple contactPrefs with their each Preference. In addition, in your Contact model, you specify the has_many as preferences .
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'primary_key' => 'contactid',
'class_name' => 'ContactPref'
),
array(
'preferences',
'foreign_key' => 'prefid',
'primary_key' => 'prefid',
'through' => 'prefs',
'class_name' => 'Preference'
)
);
Edit Through Modification:
Try the following Contact model
<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';
static $has_many = array(
array(
'prefs',
'foreign_key' => 'contactid',
'class_name' => 'ContactPref'
),
array('preferences',
'through' => 'prefs',
'class_name' => 'Preference',
'primary_key' => 'prefID')
);
}
im going through http://book.cakephp.org/2.0/en/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.html <--- this tutorial and guess what? ;) it doesnt work, im Using cakephp 2.2.2... i get next error
Undefined index: id [CORE/Cake/Model/AclNode.php, line 140]
AclNode::node() - Couldn't find Aro node identified by
"Array ( [Aro0.model] => User [Aro0.foreign_key] => )
" Error: An Internal Error Has Occurred.
this error is becouse i dont get my group_id in my User model
public function bindNode($user) {
return array('model' => 'Groups', 'foreign_key' => $user['Users']['groups_id']);
}
So in this function $user var contains only username and password... and no group_id's
Please help guys...
Model name must be singular.
'model' => 'Group'
$user['Users']['group_id'] //in your DB, put "group_id" to, not "groups_id"
Make sure that the Model you've created is "Group". :)
I've just encountered this problem. Tracing the code back the issue was that my model was called 'AdminUser' and not the default, 'User'. Altering the userModel setting solved it for me.
public $components = array(
'Acl',
'Auth' => array(
'authorize' => array(
'Actions' => array(
'actionPath' => 'controllers',
'userModel' => 'AdminUser'
)
)
),
'Session'
);
Looking at your bindNode method it looks like your using a model named 'Users' rather than 'User' so setting the userModel to 'Users' might do the trick.
I hope that helps,
Pete
On a fresh 1.5.0.1 Magento install when choosing Catalog from the settings->settings menu I get the following error:
Fatal error: Undefined class constant 'RANGE_CALCULATION_AUTO' in
/my-install-dir/app/code/core/Mage/Adminhtml/Model/System/Config/Source/Price/Step.php on line 33
Checked Step.php and it does not look damaged and contains the following:
class Mage_Adminhtml_Model_System_Config_Source_Price_Step
{
public function toOptionArray()
{
return array(
array(
'value' => Mage_Catalog_Model_Layer_Filter_Price::RANGE_CALCULATION_AUTO,
'label' => Mage::helper('adminhtml')->__('Automatic')
),
array(
'value' => Mage_Catalog_Model_Layer_Filter_Price::RANGE_CALCULATION_MANUAL,
'label' => Mage::helper('adminhtml')->__('Manual')
),
);
}
}`
Anyone know this error or how to fix it?
PHP is complaining that it can't find the constant on RANGE_CALCULATION_AUTO defined on the class Mage_Catalog_Model_Layer_Filter_Price
Based on your comments above, it sounds like you already checked the file at
app/code/core/Mage/Catalog/Model/Layer/Filter/Price.php
to ensure is had the correct constant defined.
const RANGE_CALCULATION_AUTO = 'auto';
Based on that, my guess would be there's a different Price.php being loaded for this class. This can happen if
Someone's placed a different version in community or local
Someone's monkied with the include path beyond Magento's normal monkey business
Check for files at
app/community/core/Mage/Catalog/Model/Layer/Filter/Price.php
app/local/core/Mage/Catalog/Model/Layer/Filter/Price.php
If that doesn't work, add some temporary debugging code to
app/code/core/Mage/Adminhtml/Model/System/Config/Source/Price/Step.php
that uses reflection to figure out what file PHP is loading the class from
class Mage_Adminhtml_Model_System_Config_Source_Price_Step
{
public function toOptionArray()
{
//NEW LINES HERE
$r = new ReflectionClass('Mage_Catalog_Model_Layer_Filter_Price');
var_dump($r->getFileName());
//echo $r->getFileName(); // if too long for var_dump
exit("Bailing at line ".__LINE__." in ".__FILE__);
//END NEW LINES
return array(
array(
'value' => Mage_Catalog_Model_Layer_Filter_Price::RANGE_CALCULATION_AUTO,
'label' => Mage::helper('adminhtml')->__('Automatic')
),
array(
'value' => Mage_Catalog_Model_Layer_Filter_Price::RANGE_CALCULATION_MANUAL,
'label' => Mage::helper('adminhtml')->__('Manual')
),
);
}
}`
This will dump out a file path that points to the exact place PHP is loading the class from, which should get you where you need to go.