How to keep logic out of cakephp views? - model-view-controller

I understand that "views" should only display information to the user and not do any real "thinking".
If I have a field in a form that i wasnt to restrict some user levels accessing what I do is put a condition into my view:
<td style="v-align: middle;">
<?php
if ($auth['level_id'] == 6) {
echo $form->input('product_date',
array('class' => 'input-box',
'div' => false,
'label' => false,
'readonly' => 'readonly',
'style' => 'width:100px; margin-top: 8px; float:left;',
'value' => $productiondate,
'tabindex' => 3013
)
);
echo '<div style="padding-left: 10px; float:left;"><img src="/img/submit.png" border="0"/></div>';
}
else {
echo $form->input('product_date_ro',
array('class' => 'input-box',
'div' => false,
'label' => false,
'readonly' => 'readonly',
'style' => 'width:100px',
'value' => $productiondate,
'tabindex' => 3013
)
);
}
?>
</td>
What is best practice for not doing this?
Regards
Paul

You have to make that decision at some point. Wherever you put it, you need the if ($level = 6) switch somewhere. If you absolutely want to keep it out of the view, the only other possible place is the controller. The only thing you could do in the Controller is to render a different View. This is perfect in the sense that it keeps all logic out of the view, but you'll end up with a lot of duplicate code with only small differences.
What you could do:
// Controller
switch ($level) {
case 6 :
$this->render('level6_view');
break;
...
default :
$this->render('normal_view');
}
// Views
echo $this->element('standard_elements');
echo // something special for level 6
echo $this->element('rest_of_standard_elements');
This places the logic in the controller while avoiding the worst duplication. The more complex your views get the less workable this solution is though.
You could also simply abstract the decision a bit to the controller and only set flags for the view:
// Controller
$flags = array('render_foo' => false, 'render_bar' => true);
if ($level == 6) {
$flags['render_foo'] = true;
}
$this->set(compact('flags'));
This would provide better separation of internal logic. The actual decision on what to render would still be done in the View though.
The best solution for you is probably somewhere in between. The most important thing to remember is that there's nothing wrong with having logic in the view. It's virtually impossible to not have if statements in views. Views can be intelligent and full of code, that's absolutely no problem. They should just not contain any code that is concerned with anything other than outputting the data handed to it by the controller. And, naturally, the code should be as concise and readable as possible. You need to find the right balance between abstracting things to helpers, elements or entirely different view files based on the situation.

Related

Drupal ajax form, partial render

Good day.
I created custom form in my module and defined submit button like this:
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Save'),
'#ajax' => array(
'callback' => 'fmg_addbanner_ajax_callback',
'method' => 'replace',
'wrapper' => 'banner_add_wrapper'
)
);
Then outputted my form like this:
$form = drupal_get_form('fmg_banner_add_form', $region_id);
print render($form);
But ajax requests don't work. I think because of there are no needed drupal js files. How can I solve this problem? Thanks.
The normal case will be to have #ajax attached to a common input, like checkbox, text box etc.
The behavior is that when user change the input value, it'll fire your callback through ajax and send entire form over, and then you can process this form behind the scene and adjust certain things before sending the form back and change the element to the wrapper you defined.
One of the question that I had for partial form rendering is when I need to do some ajax call or inject partial form element to certain area of the page while keeping the integrity of the form rendering. (I don't know exactly what form build did, i guess i don't want to know)
Since form rendering uses the renderable array, if you just want to extract certain element, you could just do
$form = drupal_get_form('application_form');
$body = render($form['body']);
The following is a real example that i took a form and split into header, footer and the rest of form elements.
// get a form for updating application info.
$form = drupal_get_form('pgh_awards_review_application_form', $app);
$elements = array();
$form_render = render($form);
$elements = array(
'qualify' => render($form['qualify']),
'threshold_met' => render($form['threshold_met']),
'submit' => render($form['submit']),
);
if (preg_match('/<form.+div>/', $form_render, $matches)) {
$elements['header'] = $matches[0];
}
if (preg_match('/<input type="hidden".+form>/s', $form_render, $matches)) {
$elements['footer'] = $matches[0];
}
$vars['form'] = $elements;
It's definitely not pretty approach, it just serves the needs at the moment.

Laravel 3 - How to validate checkbox array, for at least 1 checked?

I'm starting to learn Laravel and still on the learning curve. Now I'm starting with Laravel 3 but will most probably switch my project into Laravel 4 once I get something working.
Now the question being, how to validate an array of checkbox, I want to validate that at least 1 inside the group is enable(checked). I read somewhere on Laravel forum that we just validate them using a required, but when I dd(input::all()) I don't see anything else but the inputs field and checkbox are not part of them...
Part of my Blade Create code for the checkbox:
<label class="checkbox">{{ Form::checkbox('changeReasons[]', 'ckbCRCertification', Input::had('ckbCRCertification'), array('id' => 'ckbCRCertification')) }} Certification</label>
<label class="checkbox">{{ Form::checkbox('changeReasons[]', 'ckbCRDesignCorrection', Input::had('ckbCRDesignCorrection'), array('id' => 'ckbCRDesignCorrection')) }} Design Correction</label>
My controller (REST) code is:
public function post_create()
{
print "Inside the post_create()";
// validate input
$rules = array(
'ecoNo' => 'min:4',
'productAffected' => 'required',
'changeReasons' => 'required'
);
$validation = Validator::make(Input::all(), $rules);
if($validation->fails())
{
return Redirect::back()->with_input()->with_errors($validation);
}
$eco = new Eco;
$eco->ecoNo = Input::get('ecoNo');
$eco->productAffected = Input::get('productAffected');
$eco->save();
return Redirect::to('ecos');
}
I also want to know the correct code for getting the checkboxes state after a validation fails, I thought I saw the Input::had(checkBoxName) somewhere but that doesn't seem to work, I'm probably not using it correctly and I'm getting a little confuse on that since all example I see are for inputs and nothing else. I assume the validation is roughly the same in L4, would it be?
Going back on this project and making some more researches, I have found the best way for this problem is the following.
My blade view:
<div class="control-group row-fluid">
<?php $arrChangeReasons = Input::old('changeReasons', array()); // array of enable checkboxes in previous request ?>
<label class="checkbox">{{ Form::checkbox('changeReasons[]', 'certification', in_array('certification', $arrChangeReasons)) }} Certification</label>
<label class="checkbox">{{ Form::checkbox('changeReasons[]', 'designCorrection', in_array('designCorrection', $arrChangeReasons)) }} Design Correction</label>
</div>
The explanation of the blade view is a 2 steps process, after a validation occur, is the following:
Pull the checkbox array (in my case 'changeReasons[]') with Input::old
From that array we can then search for individual checkbox and see if they are in there, if they are then change the checkbox as a checked state. That is the job of the in_array() function, returning a true/false will change the state of the checkbox.
My controller (REST) code is exactly as it was written in my question at the beginning. For more information, defining $rules = array('changeReasons' => 'required'); will make sure that at least 1 of the checkboxes is checked.
Please remember that Checkboxes need a value like .
It the Checkbox is checked Input::get('foo') will return 1, but if it is unchecked it will return nothing, because it is not in the post-array.
I'm using this code:
if(Input::get('foo')){
$bar->is_foo = 1;
}
else{
$bar->is_foo = 0;
}

Zend forms: How to surround an image-element with a hyperlink?

How do I surround an image element inside a Zend form with an HTML <a href>-Tag? I specifically don't want a JS onClick()-Event as it seems not to work properly in my case (see question Zend: Redirect from form without validation).
This is how I add the image element inside the form, right now the hyperlink is displayed as Text "Link" right next to the image:
$this->addElement('image', 'btnBack', array (
'name' => 'btnBack',
'id' => 'btnBack',
'label' => '',
'title' => 'Go back',
'alt' => 'Go back',
'src' => '/img/undo.png'
));
$this->getElement('btnBack')
->setDescription('Link')
->setDecorators(array(
'ViewHelper',
array('Description', array('escape' => false, 'tag' => false)),
array('HtmlTag', array('tag' => 'dd')),
array('Label', array('tag' => 'dt')),
'Errors',
));
This sounds like a bad idea. From the look of your other question, all you want is an image 'button' that goes to a specific URL. You shouldn't try and do this with an image input, since this is a type of submit button, and so clicking it will submit the form. If you're trying to override this with javascript or wrap the whole thing with a HTML link you're trying to solve the wrong problem.
The easiest approach is probably to use a button input. You can then style it with CSS to make it look like an image, and add your javascript onclick action to make it do what you want. Since input type="button" elements don't do anything onclick by default, you shouldn't have any of the problems you had before.
$this->addElement('button', 'btnBack', array(
'id' => 'btnBack',
'label' => 'Go back',
'onclick' => "window.location='/admin/groupoverview'"
));
then in your stylesheet (replace the width and height with the correct image dimensions):
#btnBack {
background-image: url(/img/undo.png);
width: 123px;
height: 123px;
border: 0;
text-indent: -1000px;
cursor: pointer;
}
the text-indent shifts the text label out of view. If you absolutely don't want these rules in your stylesheet you could put them inline on the form element by passing a style attribute instead.

Cakephp with Ajax, this.element.setAttribute is not a function

I'm trying to make a stackoverflow like tags system.
I followed this tutorial (in French): http://www.formation-cakephp.com/34/autocomplete-en-ajax which uses Prototype and Scriptaculous. Of course, I adapted it to my project
I get the following error:
this.element.setAttribute is not a function : controls.js Line 86
which corresponds to
this.element.setAttribute('autocomplete','off');
in the control.js file
I'm really new to Ajax, so I don't have a clue on what I'm doing (wrong)...
If you need some code from any file, let me know!
view.ctp:
<div class="input">
<label>Tags :</label>
<?php e($ajax->autoComplete(
'Tag.tag',
'/tags/autocomplete',
array(
'minChars' => 3,
'indicator' => 'ajaxloader'
)
)); ?>
<div id="ajaxloader" style="display:none;">
Chargement...
</div>
Controller:
function autocomplete()
{
$recherche = utf8_decode($this->data['Tag']['tag']);
$tags = $this->Tag->find(
'all',
array(
'fields' => 'DISTINCT tag',
'conditions' => "tag LIKE '$recherche%'",
'order' => 'tag',
'limit' => 10
)
);
$this->set(compact('tag', 'recherche'));
}
jQuery, scriptaculous, & prototype don't play well together but you can resolve this issue by putting jQuery in no-conflict mode.
var $j = jQuery.noConflict();
// $j is now an alias to the jQuery function; creating the new alias is optional.
Now instead of using the $ to for jQuery use $j so for example:
$j(document).ready(function() {
$j( "div" ).hide();
});
For more information on avoiding jQuery conflicts refer to the following: https://learn.jquery.com/using-jquery-core/avoid-conflicts-other-libraries/
It appears that scriptaculous doesn't play well with j-query. When I removed the j-query link I stopped getting an error. This definitely isn't an ideal solution, but I thought I'd share my discovery.

Cakephp 1.3, Weird behavior on firefox when using $this->Html->link

Greetings,
I am getting a very weird and unpredictable result in firefox when using the following syntax:
$this->Html->link($this->Html->div('p-cpt',$project['Project']['name']) . $this->Html->div('p-img',$this->Html->image('/img/projects/'.$project['Project']['slug'].'/project.thumb.jpg', array('alt'=>$project['Project']['name'],'width'=>100,'height'=>380))),array('controller' => 'projects', 'action' => 'view', $project['Project']['slug']),array('title' => $project['Project']['name'], 'escape' => false),false);
OK I know it is big but bear with me.
The point is to get the following output:
<a href="x" title="x">
<div class="p-ctp">Name</div>
<div class="p-img"><img src="z width="y" height="a" alt="d" /></div>
</a>
I'm not sure if this validates correctly both on cakephp and html but it works everywhere else apart from firefox.
You can actually see the result here: http://www.gnomonconstructions.com/projects/browser
To reproduce the result use the form with different categories and press search. At some point it will happen!!
Although most of the time it renders the way it should, sometimes it produces an invalid output like that:
<div class="p-cpt">
name
</div>
<div class="p-img">
<img src="x" width="x" height="x" alt="x" />
</div>
Looks like it repeats the link inside each element.
To be honest the only reason I used this syntax was because cakephp encourages it.
Any help will be much appreciated :)
I am guessing that the name of some projects is null. According to the documentation, if you pass null as the second argument to the div() method, it will not generate the closing tag (and the resulting markup will be invalid).
The example of invalid markup that you pasted above appear to have come from Firebug rather than Page Source. Use Page Source to view the actual markup sent to the browser. The anchor tag is not repeated.
I rewrote your code to better see what happens. Copy it into one of your views, change 'My Project' to null, and notice how it will affect the $name_div variable:
<div class="p-cpt">My Project</div> will turn into <div class="p-cpt">.
<?php
$project['Project']['name'] = 'My Project';
$project['Project']['slug'] = 'my-project';
$image = $this->Html->image(
'/img/projects/' . $project['Project']['slug'] . '/project.thumb.jpg',
array(
'alt' => $project['Project']['name'],
'width' => 100,
'height' => 380
)
);
$name_div = $this->Html->div('p-cpt', $project['Project']['name']);
$image_div = $this->Html->div('p-img', $image);
$link = $this->Html->link(
$name_div . $image_div,
array(
'controller' => 'projects',
'action' => 'view',
$project['Project']['slug']
),
array(
'title' => $project['Project']['name'],
'escape' => false
)
);
var_dump($image);
echo 'Notice what happens below if project name is null.';
var_dump($name_div);
var_dump($image_div);
var_dump($link);
echo $link;

Resources