Cakephp View Helper - cakephp

Whenever I'm displaying a user's name in my view file, I use:
echo $user['User']['name'];
The users table also has a 'company_name' field. If the user has a company name listed (in most cases, but not all), I would like to display their company name (and if they do not, I want to use the 'name' field). The code would look something like:
if(isset($user['User']['company_name']) && $user['User']['company_name'] != '') {
echo $user['User']['company_name'];
} else {
echo $user['User']['name'];
}
What is the proper way to handle this throughout multiple view files? Should I create a helper for it?

Being that this particular use case is such a small bit of presentation logic it would make more sense to me to build this within the scope of an Element rather than a Helper.
Now if at some point down the road (or now even) you decide that there is more functionality that you would like to extend similar to this presentation logic, perhaps in the form of a UsersHelper that would define various pieces of display logic relevant to the current user, then it would be wise to encapsulate that functionality within a Helper.
Here's a good example to get you started with Elements:
// In src/Template/Element/name_display.ctp
if(isset($user['User']['company_name']) && $user['User']['company_name'] != '') {
echo $user['User']['company_name'];
} else {
echo $user['User']['name'];
}
In your view file:
// Echo this wherever you need the name displayed
echo $this->element('name_display', [
'user' => $user // Sets the $user viewVar in the element
]);
And for sake of completeness, in your controller:
$this->set('user', $user);
In your spare time it would be a good idea just to check out the manual on some of the core helpers that are included with Cake to get an idea of how they are constructed to get a better idea of use cases in which you would want to create a Helper.
2.x Elements Documentation
3.x Elements Documentation
2.x Helpers Documentation
3.x Helpers Documentation
Good luck!

Related

elements or view extensions or something else? reference app?

In cakephp [2.2] I have baked everything and my "people" view is quite busy with relations and phones and addresses and other related data. I do want all of that information visible in the people view, though not quite in the baked layout.
How should I handle those portions of the related data? I'm not sure if I should use elements or extended views or plugins or what, I'm kinda new to this and the documentation wasn't clear to me (at my level) which should be used when. The baked code seemed to be a monolithic approach, so I didn't get much help looking there.
Once the user chooses to edit a phone number (for instance) from the listing on the person view, it takes them to the phone edit view and then returns them to the phone listing (index view) and not the person view that they were on. How do I get them back to the person view instead?
The blog example they provide is nice, but is there a "reference" application somewhere for cakephp that demonstrates best practices on a wide variety of their features? I couldn't find one, or anything more than just a simple app example.
Thanks, I appreciate the guidance.
This is a rather broad question, but I'm going to try and answer it. I'm not sure how advanced you're programming knowledge is, so forgive me if I'm rehashing things you already know. First, this article was a great help when I started to use the framework for the first time as it explains what code should go where and why. It's the closest I've seen to a "reference application", which would actually be a great learning tool. You could try and have a look at some of the higher profile Cake applications, like Croogo (a Cake-based CMS). But the codebase is bound to be a little bit complex.
Personally I would use elements when you want to actually reuse them in different views. The problem however, is feeding the element its data. There's a method called requestAction, but even the manual states that this should be used with moderation and in combination with caching. The problem is that using a lot of requestAction calls in different elements litters your Controllers with methods and doesn't adhere to the "Skinny Controllers, Fat Models" mantra.
I would put most of the related data calls in their respective Models and call those Model methods from the Controller and feed them to the View. So let's say you want the 10 latest PhoneNumbers and related Users.
You would have a method in your PhoneNumber model which returns an array of users and their phonenumbers. Use the Containable behaviour to limit the number of related models which are returned. The code below is an example, so the practical implementation might vary:
public function getRecentPhoneNumbers($limit=10) {
$phoneNumbers = array();
$phoneNumbers = $this->find('all', array(
'limit' => $limit,
'contain' => array('User'),
'order' => 'PhoneNumber.id DESC'
));
return $phoneNumbers;
}
If the PhoneNumber and User model are properly related you would be able to call getRecentPhoneNumbers() from the User model:
$this->PhoneNumber->getRecentPhoneNumbers(10)
Or from the Users Controller:
$this->User->PhoneNumber->getRecentPhoneNumbers(10)
Say you have an element which shows a list of those 10 numbers and it accepts a variable called $recentPhonenumbers, you then set the variable in the relevant UsersController method with the returned array from the getRecentPhoneNumbers call:
$this->set('recentPhonenumbers', $this->User->PhoneNumber->getRecentPhoneNumbers(10));
This will make it available to the View that contains the element.
The extended views are relatively new (from Cake 2.1 and onwards) and I haven't used them, but seem a great way to create conditional markup.
As for the second question, redirecting the user to the person view, rather than the index view. This is a matter of adjusting the redirect (see the manual for more details) in the edit() method of the Controller. Standard baked edit() methods accept an $id parameter you can use this to redirect to the view() (which probably also accepts an $id paramater).
So the redirect probably looks something like this:
$this->redirect(array('controller' => 'users', 'action' => 'index'));
Change it to:
$this->redirect(array('controller' => 'users', 'action' => 'view', $id));

Cakephp Plugin model name prefix

I have a plugin with the name 'Admin' and my methods are like index, add etc. Since I want to apply unique model names and controller names (for ACL implementation), I prefixed all controllers and models with the plugin name (please see cakephp plugin model/controller cache issue with main model/controller). After that, Im facing the following problems. I have a table called users and I am using cakephp2.0.
1) I have to access the url with domain.com/admin/admin_users/index or admin/content/admin_index, instead I want to access by admin/users/index or admin/content/index. How to set this in routes in a general way so that it will be applied to all ?
2) In view, it is showing undefined index User (I have baked the views before). Everything is coming as AdminUser. After setting public $alias = 'User' this issue has been solved. Is this correct ?
3) In controller, I have to change "$this->User->some var/fn " to $this->AdminUser->some var/function is all places. Any way to solve this ?
Anything wrong with plugin name giving as admin (I have not set admin routing) ?
You can safely name your plugin admin, this is by no means a reserved word. However, Cake doesn't take plugin names into account when parsing model / controller names, so AdminUsersController and AdminUser are actually going to be seen as classes belonging to some abstract AdminUser. So to solve all your problems using only Cake magic you'd have to actually name them User and UsersController, and hope these names don't clash. If this is not an option, you can provide some of your own magic to solve these things:
1) How to alias a controller is one of the route configuration examples in the cookbook
2) Yups, if you want to use "User" as a key, that is correct.
3) Not really using only Cake magic, because the $uses variable doesn't support aliasing. You can, however, take advantage of the new lazy-loading of 2.0, by doing something like this in your AdminUsersController:
<?php
public function __get($name) {
// Note, the isset triggers lazy-loading. Check out Controller::__isset()
// if you want to see how that works.
if ($name == 'User' && isset($this->AdminUser)) {
// Assign to admin user here to bypass the next time
return ($this->User = $this->AdminUser);
}
// Delegate to CakePHP's magic getter
return parent::__get($name);
}

Cakephp Helpers in Views and $this

I'm trying to determine what the best standard is for using helpers in views whether is should be
echo $form->input();
or
echo $this->Form->input();
In the CakePHP manual ver 1.2 the Helper class is accessed by the helper object directly, whereas in the 1.3 book the helper object is accessed through the View.
Does this matter?
Leo
It really only matters because of the possibility of having a collision that will "wipe out" your access to the helper. Say I had a model named Form and decided to do something like this in my view after getting many records.
foreach ($forms as $form) {
echo $form['Form']['name'] . '<br/>';
}
See what happened there? I accidentally just overwrote the $form variable, basically losing my FormHelper.
The standard is to now access all helpers via $this in the view.

How to pre-populate a saveAll form?

I have a form which looks like this:
Delete
[Publisher One ] []
[Publisher Two ] []
[Publisher Three] []
Add[ ]
So basically, every Publisher appears on the page in its own field. I can modify any of the Publisher names, delete any of the Publishers, or add a new publisher, all on one form, simply by saveAll-ing the form. I know that this will not hold up under 10,000 rows, but I am using CakePHP to remake an existing tool, and I am sure there will be a manageable number of rows.
The problem is that on first load, I have to pre-populate the fields. Now, from the CakePHP book, I am supposed to create the form with Model.n.field. However, the data I pull using a $this->Model->find('all') is in $data[n][Model][field] form. Am I going to have to mangle the data myself to get it in $data[Model][n][field] form, or is there an easy way to do that from within the find command, or perhaps a helper function to turn it from one into the other?
The Set class might be able to do the reprocessing for you - see CakeBook and an example. Also other Set class methods might be useful for you.
The difficulties in moving data in different forms around in Cake isn't the smoothest, I agree. But why don't you just do this:
pull your data in the controller. $data is an array with the nth record as the first index; then Modelname, then fieldname (sorry for the confusing wording -- this is just how you've done it above).
in your view:
foreach( $data as $publisher-id => $record )
{
echo '<tr><td>' . $record['Model']['publisher_name'] . '</td>';
echo '<td>' . $form->input( 'Model.publisher_id', array( 'value' => $model['Modelname']['publisher_id']) ) . '</td></tr>';
}
So I don't really think you need to mangle the data, or the arrays returned from the find operation. Try to just think about how you're going to output it, and you have to be a little clever about it.

How do you perform Form validation without a model in Cakephp?

I need to perform some validation. I don't have the model in the application.
Does anyone know how to do the validation without a model?
Can you show me using a small sample or statement?
Honestly, I'd create a model just for the validation. You can create a model that doesn't use a table by adding
var $useTable = false;
And then create a validation array with rules for each field you want to validate:
var $validate = array('login' => 'alphaNumeric','email' => 'email','born' => 'date');
Then, in your controller, do something like:
$this->MyModel->set($this->data);
if($this->MyModel->validates()){
// do stuff with valid data
}
If you really, really can't use a model, then you'll have to simply loop over each value in $this->data in your controller action and validate it against a regular expression or use the Validation::[rule]() stuff, like:
if(Validation::email($someThingThatMightBeAnEmailAddress)){
// do stuff with valid email address.
}
You can perform validation of form data in CakePHP without having to create a model.php file. There are many times when I need to do this, and storing model.php files that do nothing more then validation is a poor usage of the model design pattern.
Another problem with CakePHP is that sometimes validation rules are common across multiple models. It would be nice to move validation out of the model, much in the way behaviors are to their own subfolder. That way we can re-use them or use them without a model.
Another problem with validation is that it's dependent upon the model alias. If you have a model called "Email" without a table to perform validation, then the posted form must also use "Email". If the form uses a alias different from the controller, then you have to set the action. A lot of extra steps just to do validation. You can't re-use that model again if your form uses a different model.
So here is my alternative approach.
In your controller's action that receives the posted form data. You can create a default CakePHP model, add some validation rules and then use that model for validation.
An example action might look like this;
function edit()
{
$model = ClassRegistry::init(array('class'=>'Email','table'=>false,'type'=>'Model'));
if(!empty($this->data))
{
$model->validate = array(
'subject'=>array(
'rule'=>'notEmpty',
'required'=>true
),
'message'=>array(
'rule'=>'notEmpty',
'required'=>true
)
);
if($model->save($this->data))
{
// validation was successful, but no data was actually saved
}
}
}
The key here is the creation of an automatic model by CakePHP.
$model = ClassRegistry::init(array('class'=>'Email','table'=>false,'type'=>'Model'));
The above attempts to find a model by Email in the applications model folder. When it is not found CakePHP will auto-create an in memory model for that class. Since we set the value of 'table' to false, then this should tell CakePHP that this model doesn't use a table.
This works as long as there really isn't a email.php file in the applications model folder. Once this model is created in memory. It's accessible from the built in Form help. That means validation errors will be passed correctly to the view.
Here is an example view file.
<?php echo $this->Form->create('Email',array('action'=>array('controller'=>'mycontroller','action'=>'edit'))); ?>
<?php echo $this->Form->input('subject'); ?>
<?php echo $this->Form->input('message',array('type'=>'textarea')); ?>
<?php echo $this->Form->submit(); ?>
The view will now render the validation errors from the Email model using the Form helper. This is because CakePHP class registry has saved a copy of the EMail auto model in memory that the Form helper will access.
If you want to use custom validation rules, then you will have to put the callback methods in the app_model.php file.
These methods tested in CakePHP 1.3
#ThinkingMedia's answer got me going in the right direction, but $model->save($this->data) was returning false for me unfortunately, even when the form was valid. I'm using CakePHP 2.3.9 for reference.
It turned out that even with 'table' => false parameter set, the returned$success of save() was based on a $count > 0 of the rows that were created/updated/modified. In my table-less case, this meant a $count of 0 and $success was false. I ended up blending the referenced answer with this similar question's solution to have validation work properly without a model file:
function edit()
{
$model = ClassRegistry::init(array('class'=>'YourFormName','table'=>false,'type'=>'Model'));
if($this->request-is('post'))
{
$model->validate = array(
'some_field'=>array(
'rule'=>'notEmpty',
'required'=>true
),
'another_field'=>array(
'rule'=>'notEmpty',
'required'=>true
)
);
$model->set($this->request->data)
if($model->validates($this->request->data) && empty($model->validationErrors))
{
// validation was successful, but no data was actually saved
}
}
}
Came across this question since I also had a similar issue. I have a form that needs to collect data and generate a PDF. So there is no data saving involved nor there is a maching model class. The PDF is a user contract and the user will fill the online form and the data filled will be used to generate the PDF which they must print and mail back. But I need to validate whether the fields are not empty, whether email is really an email format, and date inputs are really date inputs.
First I did without a model class then saw this quesion. Now I'm using a dummy model class to have my validations put in there since the code in controller action looks much neat.
Class Validation which is a subclass of Object is used by model class to perform validation against validation rules specified in it.
One can directly instantiate Validation class inside any controller or model and use its methods for performing validation on any data, not only inputs from forms.
I think my first question would be this: if you don't have a model...what are you validating? Typically data collection would be done to populate a model. If you're using an alternative data repository (file, web services, etc.), a model would still be the appropriate way to access and manipulate that data.
In short, in order to better answer this, I think a little more context would be helpful and maybe even necessary.

Resources