specify return fields for all find calls on model in cakephp - cakephp

I can't believe I don't remember how to do this, but how do I specify in my model the default fields that are returned with the find() methods? I can't find on google how to do this, or at least I don't know the wording to search for.

What you will most likely need to do, is to check if the fields key exists in a beforeFind() method in your model.
If the fields key is not set, you can set it to $this->fields in your native models, and create the beforeFind() in your AppModel, then you can instruct that method to use the $this->fields array from your models.
UPDATE
// AppModel.php
parent function beforeFind($queryData = array()) {
if (empty($queryData['fields']) && !empty($this->fields)) {
$queryData['fields'] = $this->fields;
}
return $queryData;
}
// And in your Model:
public $fields = array(
'Alert.id'
);
This will check for existence of a fields array, and will then check for existence of a $this->fields property. If it does exist, it will apply it to the query data and return that modified query data to the beforeFind() - this will change your find.
Adjust it to fit your needs, and good luck!

Related

access model data in custom validation rule

I have custom validation rule:
public function customRule($check)
{
}
Inside this rule I would like to access some model data (in database). Of course I can do it like this:
$this->id = 23;
$this->read();
But then all the data in current model will be overidden by read function (I mean $this->data[$this->alias][...] is overridden.
How I can get this data?
Use a regular
$result = $this->find('first', array('conditions' => array($this->alias . '.' . $this->primaryKey => $id));
with the id in the find conditions. And work with the result, it is not overriding the data property.
Just to note that if you want to get the full record of the data that is currently being validated it is always accessible in $this->data inside the validation rule as opposed to $check which contains only the data in the currently validated field.
If you need to validate based on something that is stored in the DB, you can use $this->find() or any of the Model's functions as you are in the Model.
I support #burzum 's answer +1.

CakePHP: afterFind is weird with associations

So afterFind works fine and dandy when I'm within the corresponding model/controller. However, when calling an associated model, the data sent to the afterFind callback is formatted differently. This causes afterFind to crap out because it can't find the same array indexes it did when just working within the original model/controller.
Anyone know why, or what a fix might be?
$primary may not be very helpful; I've found that it is always false when using ContainableBehaviour beyond the first depth:
$this->Model->find('first', array(
'contain' => array(
'SecondaryModel' => array(
'TertiaryModel',
),
),
));
If you're setting a value based on a related model, you can check for its presence to deal with either structure like this:
function afterFind($results, $primary) {
if (isset($results['TertiaryModel'])) {
$results['secondary_model_field'] = 'value';
}
else {
foreach ($results as &$result) {
if (is_array($result) && isset($result['TertiaryModel'])) {
$result[$this->alias]['secondary_model_field'] = 'value';
}
} unset($result);
}
return $results;
}
Alternately you may be able to just check for the location of a field on the model itself. If the field doesn't exist at the top level, you will need to iterate over the set of results.
This is what the second parameter to afterFind callback is for.
$primary tells if you if the find was called from this model directly (true), or if it was called by an associated model (false).
A note from the book:
The $primary parameter indicates whether or not the current model was
the model that the query originated on or whether or not this model
was queried as an association. If a model is queried as an
association the format of $results can differ;
Code expecting $primary to be true will probably get a "Cannot use
string offset as an array" fatal error from PHP if a recursive find
is used.
So you may need different processing logic depending on the value of $primary
It appears that Cake 2.6 includes a fix for this, ensuring that all $results arrays are consistently formatted. I've done a little testing with the RC release and it does seem to work, with arrays all being passed in the format {n}.ModelName.data.
The fix is enabled by default, but you can also revert to the legacy behaviour if need be. Just add the following to your model (or AppModel) definition:
public $useConsistentAfterFind = false;

How do I get associated models to run __construct?

Little history; I hate the fact that I can't use enums in CakePHP, but I get it. However, another pet peev I have is that my Booleans return 0 or 1 and there is no way to universally turn them to yes' and no's.
So I though I would create a little function in the afterFind method of the AppModel to do this for me. The first step I wanted to take was to identify which columns where boolean (since some columns will return zeros and ones that do not need to be converted). I devised this little peace of code:
function __construct($id = false, $table = null, $ds = null) {
parent::__construct($id, $table, $ds);
foreach($this->_schema as $col => $colDetails){
if($colDetails['type'] == 'boolean')
$this->_booleans[] = $col;
}
}
However a quick debug($this) in the model show that only the current model's boolean columns are captured. When I hit those columns directly the $this->_booleans show up but again, not those of associated models.
I've looked though the manual and the API..I see no mention as to how to approach a solution.
What am I doing wrong?
Enums are not supported by CakePHP in order to make an application database type independent. Enums are not supported by many database engines. The simplest solution for your task is:
echo $model['boolField'] ? 'Yes' : 'No';
The problem is that $this->_booleans in the AppModel only contains the schema details of the current model. In fact, the code is probably working. You should check $this->_booleans and $this->Related->_booleans, and I bet you'll find what you're looking for.

Need to make full names in cakePHP

If I have a person model with first_name and last_name, how do I create and display a full_name? I would like to display it at the top of my Edit and View views (i.e. "Edit Frank Luke") and other places. Simply dropping echoes to first_name and last_name isn't DRY.
I'm sorry if this is a very simple question, but nothing has yet worked.
Thank you,
Frank Luke
Edit for clarity: Okay, I have a function on the person model.
function full_name() {
return $this->Person->first_name . ' ' . $this->Person->last_name;
}
In the view, I call
echo $person['Person']['full_name']
This gives me a notice that I have an undefined index. What is the proper way to call the function from the view? Do I have to do it in the controller or elsewhere?
If what you are wanting is just to display a full name, and never need to do any database actions (comparisons, lookups), I think you should just concatenate your fields in the view.
This would be more aligned with the MVC design pattern. In your example you just want to view information in your database in a different way.
Since the action of concatenating is simple you probably don't save much code by placing it in a separate function. I think its easiest to do just in the view file.
If you want to do more fancy things ( ie Change the caps, return a link to the user ) I would recommend creating an element which you call with the Users data.
The arrays set by the save() method only return fields in the datbase, they do not call model functions. To properly use the function above (located in your model), you will need to add the following:
to the controller, in the $action method:
$this->set( 'fullname', $this->Person->full_name();
// must have $this-Person->id set, or redefine the method to include $person_id
in the view,
echo $fullname;
Basically, you need to use the controller to gather the data from the model, and assign it to the controller. It's the same process as you have before, where you assign the returned data from the find() call to the variable in the view, except youre getting the data from a different source.
There are multiple ways of doing this. One way is to use the afterFind-function in a model-class.
See: http://book.cakephp.org/view/681/afterFind.
BUT, this function does not handle nested data very well, instead, it doesn't handles it al all!
Therefore I use the afterfind-function in the app_model that walks through the resultset
function afterFind($results, $primary=false){
$name = isset($this->alias) ? $this->alias : $this->name;
// see if the model wants to attach attributes
if (method_exists($this, '_attachAttributes')){
// check if array is not multidimensional by checking the key 'id'
if (isset($results['id'])) {
$results = $this->_attachAttributes($results);
} else {
// we want each row to have an array of required attributes
for ($i = 0; $i < sizeof($results); $i++) {
// check if this is a model, or if it is an array of models
if (isset($results[$i][$name]) ){
// this is the model we want, see if it's a single or array
if (isset($results[$i][$name][0]) ){
// run on every model
for ($j = 0; $j < sizeof($results[$i][$name]); $j++) {
$results[$i][$name][$j] = $this->_attachAttributes($results[$i][$name][$j]);
}
} else {
$results[$i][$name] = $this->_attachAttributes($results[$i][$name]);
}
} else {
if (isset($results[$i]['id'])) {
$results[$i] = $this->_attachAttributes($results[$i]);
}
}
}
}
}
return $results;
}
And then I add a _attachAttributes-function in the model-class, for e.g. in your Person.php
function _attachAttributes($data) {
if (isset($data['first_name']) && isset($data['last_name'])) {
$data['full_name'] = sprintf("%s %s %s", $data['first_name'], $data['last_name']);
}
return $data;
}
This method can handle nested modelData, for e.g. Person hasMany Posts then this method can also attachAttributes inside the Post-model.
This method also keeps in mind that the linked models with other names than the className are fixed, because of the use of the alias and not only the name (which is the className).
You must use afterFind callback for it.
You would probably need to take the two fields that are returned from your database and concatenate them into one string variable that can then be displayed.
http://old.nabble.com/Problems-with-CONCAT-function-td22640199.html
http://teknoid.wordpress.com/2008/09/29/dealing-with-calculated-fields-in-cakephps-find/
Read the first one to find out how to use the 'fields' key i.e. find( 'all', array( 'fields' => array( )) to pass a CONCAT to the CakePHP query builder.
The second link shows you how to merge the numeric indexes that get returned when you use custom fields back into the appropriate location in the returned results.
This should of course be placed in a model function and called from there.

What is the equivalent to getLastInsertId() in Cakephp?

If I do getLastInsertId() immediately after a save(), it works, but otherwise it does not. This is demonstrated in my controller:
function designpage() {
//to create a form Untitled
$this->Form->saveField('name','Untitled Form');
echo $this->Form->getLastInsertId(); //here it works
}
function insertformname() {
echo $this->Form->getLastInsertId(); //this doesnt echo at all
}
Please suggest a way to get the functionality I want.
CakePHP has two methods for getting the last inserted id: Model::getLastInsertID() and Model::getInsertID().
Actually these methods are identical so it really doesn't matter which method you use.
echo $this->ModelName->getInsertID();
echo $this->ModelName->getLastInsertID();
This methods can be found in cake/libs/model/model.php on line 2768
Just use:
$this->Model->id;
In Cake, the last insert id is automatically saved in the id property of the model. So if you just inserted a user via the User model, the last insert id could be accessed via $User->id
id - Value of the primary key ID of
the record that this model is
currently pointing to. Automatically
set after database insertions.
Read more about model properties in the CakePHP API Docs: http://api.cakephp.org/2.5/class-AppModel.html
Edit: I just realized that Model::getLastInsertID() is essentially the same thing as Model->id
After looking at your code more closely, it's hard to tell exactly what you're doing with the different functions and where they exist in the grand scheme of things. This may actually be more of a scope issue. Are you trying to access the last insert id in two different requests?
Can you explain the flow of your application and how it relates to your problem?
You'll need to do an insert (or update, I believe) in order for getLastInsertId() to return a value. Could you paste more code?
If you're calling that function from another controller function, you might also be able to use $this->Form->id to get the value that you want.
Try using this code in your model class (perhaps in AppModel):
function get_sql_insert_id() {
$db =& ConnectionManager::getDataSource($this->useDbConfig);
return $db->lastInsertId();
}
Caveat emptor: MySql's LAST_INSERT_ID() function only works on tables with an AUTO_INCREMENT field (otherwise it only returns 0). If your primary key does not have the AUTO_INCREMENT attribute, that might be the cause of your problems.
this is best way to find out last inserted id.
$this->ModelName->getInsertID();
other way is using
$this->ModelName->find('first',array('order'=>'id DESC'))
There are several methods to get last inserted primary key id while using save method
$this->loadModel('Model');
$this->Model->save($this->data);
This will return last inserted id of the model current model
$this->Model->getLastInsertId();
$this->Model-> getInsertID();
This will return last inserted id of model with given model name
$this->Model->id;
This will return last inserted id of last loaded model
$this->id;
Try to use this code. try to set it to a variable so you can use it in other functions. :)
$variable = $this->ModelName->getLastInsertId();
in PHP native, try this.
$variable = mysqli_insert_id();
This will return last inserted id of last loaded model
$this->id;
This will return last inserted id of model with given model name
$this->Model->id;
This will return last inserted id of the model current model
CakePHP has two methods for getting the last inserted id:
Model::getLastInsertID() and Model::getInsertID().
echo $this->ModelName->getInsertID();
echo $this->ModelName->getLastInsertID();
Below are the options:
echo $this->Registration->id;
echo $this->Registration->getInsertID();
echo $this->Registration->getLastInsertId();
Here, you can replace Registration with your model name.
Thanks
Use this one
function designpage() {
//to create a form Untitled
$this->Form->saveField('name','Untitled Form');
echo $this->Form->id; //here it works
}
You can get last inseted id with many ways.Like Model name is User so best way to fetch the last inserted id is
$this->User->id; // For User Model
You can also use Model function but below code will return last inserted id of model with given model name for this example it will return User model data
$this->User->getLastInsertId();
$this->User->getInsertID();
When you use save(), the last insert ID is set to the model’s $id property. So:
if ($this->Model->save()) {
printf('Last insert ID was %s', $this->Model->id);
}
Each time a save method is called on a model, cake internally calls Model::getLastInsertId() and stores the result into model class attribute id, so after calling save() it is not necessary to call Model::getLastInsertId() or inserId(), as tha value can be directly accessed like this
$id = $this->id;// within a model
$id = $this->{$this->modelName}->id;// in a controller
After insertion of data, we can use following code to get recently added record's id:
$last_insert_id=$this->Model->id;
each time you perform an insert operation on any model, cake internally fetchesthe last insert Id and Sets to Model->id attribute.
so one can access it directly by $Model->id;,
no need to query again for lastInsertId.
I think it works with getLastInsertId() if you use InnoDB Tables in your MySQL Database. You also can use $this->Model->id
$Machinedispatch =
$this->Machinedispatch->find('first',array('order'=>array('Machinedispatch.id DESC')));
Simplest way of finding last inserted row. For me getLastInsertId() this not works.
Actually you are using the getLastInsertId or getInsertId in a wrong manner.
getLastInsertId() is meant to work only after save() method.
It will even not work after a manual insert, as cake engine is storing the mysql_insert_id under $this->_insertID inside the save method which can be retrieved via the getLastInsertId or getInsertId.
Now in your case
$this->Model->id
OR
$this->Model->find('first',array('order'=>'id DESC'))
Will do.
This is interesting, I also stumbled upon this issue. What you asked perhaps how to get the last ID of a certain model regardless of it's state, whether it's just been inserted or not. To further understand what getInsertID does, we need to take a look at the source:
Link 1: http://api20.cakephp.org/view_source/model#line-3375
public function getInsertID() {
return $this->_insertID
}
Yup, that's the only piece of code inside that function. It means that cakephp caches any last inserted ID, instead of retrieve it from the database. That's why you get nothing if you use that function when you haven't done any record creation on the model.
I made a small function to get the last ID of a certain table, but please note that this should not be used as a replacement of getLastID() or getLastInsertID(), since it has an entirely different purpose.
Add the function lastID() to the AppModel as shown below so that it can be used system wide. It has it's limit, which can't be used on model with composite primary key.
class AppModel extends Model {
public function lastID() {
$data = $this->find('first',
array(
'order' => array($this->primaryKey . ' DESC'),
'fields' => array($this->primaryKey)
)
);
return $data[$this->name][$this->primaryKey];
}
}
Original Source : Class Model
In CakePHP you can get it by:
Model::getInsertID() //Returns the ID of the last record this model inserted.
Model::getLastInsertID() //Alias to getInsertID().
$this->Model->field('id', null, 'id DESC')

Resources