I'm experiencing a difficulty in rendering sfWidgetFormDoctrineChoice (set of checkboxes) widget for a NestedSet structure.
class ModelForm extends BaseModelForm
{
public function configure()
{
$this->setWidget('relatedmodel_list', new sfWidgetFormDoctrineChoice(array(
'expanded' => true,
'multiple' => true,
'model' => 'Relatedmodel',
'table_method' => 'fetchTree'
)));
}
}
class RelatedmodelTable extends Doctrine_Table
{
/**
* Gets tree elements in one query (one root only)
*/
public function fetchTree()
{
$q = $this->createQuery('m')
->addOrderBy('m.lft');
$tree = $q->execute(array(), Doctrine_Core::HYDRATE_RECORD_HIERARCHY);
return $tree;
}
}
Now, if I just render form like this: <?php echo $form['relatedmodel_list'] ?>
It will only display form widgets (checkboxes) for first level elements of my hierarchy.
I am looking for an implementation that will allow me to iterate over widget's choices the way I would iterate over collection:
<?php foreach ($form['relatedmodel_list'] as $widget): ?>
<?php echo $widget->render() ?>
<?php foreach ($widget->getChildren() as $child_widget): ?>
<?php echo $child_widget->render() ?>
<?php endforeach; ?>
<?php endforeach; ?>
I'm using sfWidgetFormTree to display my nestedSet. If you really want to display your tree in a flat way don't use HYDRATE_RECORD_HIERARCHY.
The widget linked is very convinient, you just have to provide a choices array like this :
$choices = array(
1=> array('label'=>'test', 'children'=>array(
2=> array('label'=>'test2', 'children'=> array(
3=> array('label'=>'test3'),
4=> array('label'=>'hans')
)),
5=> array('label'=>'wurst')
)),
6=>array('label'=>'letzter')
);
If anyone cares, I think I found a wonderful solution which allows you to recursively iterate over checkboxes in template. The idea behind it is that you configure 'relatedmodel_list' widget as a single checkbox and render it many times in your template (while iterating over relatedmodel collection).
class ModelForm extends BaseBookForm
{
public function configure()
{
$this->setWidget('relatedmodel_list', new myWidgetFormInputCheckbox());
}
}
Checkboxes now have incorrect name and value attributes. This can be fixed very easily:
class myWidgetFormInputCheckbox extends sfWidgetFormInputCheckbox
{
public function render($name, $value = null, $attributes = array(), $errors = array())
{
//fix value checking
if (in_array($attributes['value'], (array)$value))
{
$attributes['checked'] = 'checked';
}
//fix name for multiple
$name = $name . "[]";
return parent::render($name, null, $attributes, $errors);
}
}
Now we can recursively render our form widget in template:
//_form.php
<ul>
// Model::getRelatedTree() is proxy to Relatedmodel::fetchTree()
<?php include_partial('node', array('node' => $form->getObject()->getRelatedTree(), 'form' => $form)) ?>
</ul>
//_node.php
<?php foreach ($node as $item): ?>
<li>
<?php echo $form['pages_list']->render(array('value'=>$item->id)) ?>
<?php echo $form['pages_list']->renderLabel((string)$item) ?>
<?php if (isset($item['__children']) && count($item['__children']) > 0): ?>
<ul>
<?php include_partial('node', array('node' => $item['__children'], 'form' => $form)) ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
Related
I am using cakephp 3. I have to create a view for a Group in which in every Subject, there will be Lessons but in my case:
Group hasMany subjects
Subject hasMany lessons
Here is my code in the GroupsController:
public function mygroup()
{
$this->loadModel('Lessons');
$group = $this->Groups->get($this->Auth->user('group_id'), [
'contain' => ['Subjects', 'Users']
]);
$lesson = $this->Lessons->find('all');
$this->set('lesson', $lesson);
$this->set('_serialize', ['lesson']);
$this->set('group', $group);
$this->set('_serialize', ['group']);
}
I have to load lessons model because it is not associated with group, in mygroup.ctp view:
<?php if (!empty($group->subjects)): ?>
<?php foreach ($group->subjects as $subjects): ?>
<?= h($subjects->subject) ?>
<?= h($subjects->id) ?>
<?= h($subjects->created) ?>
<?php foreach ($lesson as $lesson): ?>
<?php if ($subjects->id == $lesson->subject_id): ?>
<?= h($lesson->lesson) ?>
<?= h($lesson->subject_id) ?>
<?= h($lesson->created) ?></div>
<?php endif; ?>
<?php endforeach; ?>
<?php endforeach; ?>
<?php endif; ?>
I want to view lesson just below of its subject where it belongs. I don't know but the for each loop for the subject is working but the for each loop of lessons it only work for the first subject it displays:
Something like this:
Subject1
Lesson1 for sub1
Lesson2 for sub1
Lesson3 for sub1
Subject2
Subject3
Subject4
Subject5
The lessons are not showing in the subject 2 to 5 yet there should be.
Can u help me with this? Or is there any other way to do it? If there someone. Very much appreciate it. Thank you
In each module in my application I'll have a main content section and a sidebar menu.
In my layout I have the following...
<div id="main" class="span8 listings">
<?php echo $this->content; ?>
</div>
<div id="sidebar" class="span4">
<?php echo $this->sidebar; ?>
</div>
My controllers all return a single ViewModel which specifies the content (see below) but how do I get it to also populate the sidebar?
public function detailsAction()
{
*some code to populate data*
$params = array('data' => $data);
$viewModel = new ViewModel($params);
$viewModel->setTemplate('school/school/details.phtml');
return $viewModel;
}
I've got a feeling I am doing something fundamentally wrong here.
You can include "sub templates" by using the partial view helper
<div id="main" class="span8 listings">
<?php echo $this->content; ?>
</div>
<div id="sidebar" class="span4">
<?php echo $this->partial('sidebar.phtml', array('params' => $this->params)); ?>
</div>
In a controller you could use view models nesting and the layout plugin:
public function fooAction()
{
// Sidebar content
$content = array(
'name' => 'John'
'lastname' => 'Doe'
);
// Create a model for the sidebar
$sideBarModel = new Zend\View\Model\ViewModel($content);
// Set the sidebar template
$sideBarModel->setTemplate('my-module/my-controller/sidebar');
// layout plugin returns the layout model instance
// First parameter must be a model instance
// and the second is the variable name you want to capture the content
$this->layout()->addChild($sideBarModel, 'sidebar');
// ...
}
Now you just echo the variable in the layout script:
<?php
// 'sidebar' here is the same passed as the second parameter to addChild() method
echo $this->sidebar;
?>
// Module.php add it is
use Zend\View\Model\ViewModel;
public function onBootstrap($e)
{
$app = $e->getParam('application');
$app->getEventManager()->attach('dispatch', array($this, 'setLayout'));
}
public function setLayout($e)
{
// IF only for this module
$matches = $e->getRouteMatch();
$controller = $matches->getParam('controller');
if (false === strpos($controller, __NAMESPACE__)) {
// not a controller from this module
return;
}
// END IF
// Set the layout template
$template = $e->getViewModel();
$footer = new ViewModel(array('article' => "Dranzers"));
$footer->setTemplate('album/album/footer');
$template->addChild($footer, 'sidebar');
}
I am using sfDoctrinePager in my module. I search the table on one or more criteria. First time the result displayed is correct but after it if I click on page 2 or more it again gives me the whole resultset and my search resultset is lost.
I am facing this issue for the first time, even though I have been using sfDoctrinePager since a long time.
I get this Array from my search form
(
[field_type] => log_type
[field_value] => ABC
[is_active] => on
)
I send this array variable to my model:
$getQuery = $objMgr->getSearchQuery($request->getPostParameters());
** The query which runs in model is:
public function getSearchQuery($arrSearchValues)
{
$q = Doctrine_Query::create()
->select('u.*')
->from('SomsConfigValues u');
->where('u.deleted_at IS NULL');
if(isset($arrSearchValues['is_active']) && $arrSearchValues['is_active'] == 'on'){
$q->where('u.is_active = ?', 1);
}
if(isset($arrSearchValues['field_type']) && $arrSearchValues['field_type'] != ""){
$revertChg = ucwords(str_replace("_", " ", $arrSearchValues['field_type']));
$q->andWhere('u.field_type = ?', $revertChg);
}
if(isset($arrSearchValues['field_value']) && $arrSearchValues['field_value'] != ''){
$q->andWhere('u.field_type = ?', $arrSearchValues['field_value']);
}
return $q;
}
First time I get the perfect searched result, but second time (when I click on page 2 it gives me whole resultset).
My action is:
public function executeIndex(sfWebRequest $request)
{
$objMgr = AdminFactory::adminObject();
$getQuerys = $objMgr->getSearchQuery($request->getPostParameters());
$this->config_values = $getQuerys;
$this->pager = new sfDoctrinePager('configValues', sfConfig::get('app_max_row_display'));
$this->pager->setQuery($this->config_values);
$this->pager->setPage($request->getParameter('page', 1));
$this->pager->init();
}
The template where I am using this pagination is:
<div class="grid_footer">
<div style="padding:5px;">
<?php if(count($pager->getLinks())) : ?>
<div class="tableControl">
<div class="pagination_inner">
<?php if(!$pager->isFirstPage()){ ?>
<?php echo link_to1(image_tag('/images/first.png'), 'configValues/index?page='.$pager->getFirstPage()) ?>
<?php echo link_to1(image_tag('/images/previous.png'), 'configValues/index?page='.$pager->getPreviousPage()) ?>
<?php } ?>
<div class="numbers">
<?php if ($pager->haveToPaginate()): ?>
<?php $links = $pager->getLinks(); foreach ($links as $page): ?>
<?php echo ($page == $pager->getPage()) ? $page : link_to($page, 'configValues/index?page='.$page) ?>
<?php endforeach ?>
<?php endif ?>
</div>
<?php if(!$pager->isLastPage()){ ?>
<?php echo link_to1(image_tag('/images/next.png'), 'configValues/index?page='.$pager->getNextPage()) ?>
<?php echo link_to1(image_tag('/images/last.png'), 'configValues/index?page='.$pager->getLastPage()) ?>
<?php } ?>
</div>
</div>
<?php echo $pager->getNbResults() ?> results (page <?php echo $pager->getPage(); ?>/<?php echo count($pager->getLinks()); ?>)
<?php endif; ?>
</div>
</div>
I was having exactly the same problem. The only way around this I found is as you said, to store the query. Instead of using the session, create an attribute in the user to store this.
in the action class put something like...
$this->getUser()->setAttribute('searchQuery', $getQuery);
This value persists through the session, so you can retreive it using e.g.
$storedQuery = $this->getUser()->getAttribute('searchQuery');
You can view the User attributes via the config item on the dev toolbar.
I have an embeddedForm that I am trying to configure the widgets for.
Currently I am just outputting the form in a _form.php template like:
<?php echo $form ?>
This is great, but I'd like to have my form fields in a particular order, so I thought I'd try:
<?php echo $form['firstname']->renderRow() ?>
<?php echo $form['lastname']->renderRow() ?>
<?php echo $form['email_address']->renderRow() ?>
This gives me an invalid widget error.
Now I have 2 forms, one is a basic form, that simply embeds another form.
<?php
class labSupportForm extends sfGuardUserAdminForm
{
public function configure()
{
$form = new labSupportProfileForm($this->getObject()->getProfile());
$this->embedForm('profile', $form);
unset($this['is_super_admin'], $this['is_admin'], $this['permissions_list'], $this['groups_list']);
$this->widgetSchema['profile'] = $form->getWidgetSchema();
$this->validatorSchema['profile'] = $form->getValidatorSchema();
}
public function save($con = null)
{
$user = parent::save($con);
if (!$user->hasGroup('Lab Support'))
{
$user->addGroupByName('Lab Support');
$user->save();
}
return $user;
}
}
and:
<?php
class labSupportProfileForm extends sfGuardUserProfileForm
{
public function configure()
{
unset($this['email_new'],
$this['validate_at'],
$this['validate'],
$this['address_1'],
$this['address_2'],
$this['city'],
$this['country'],
$this['postcode'],
$this['created_at'],
$this['updated_at'],
$this['user_id'],
$this['is_super_admin'],
$this['is_admin'],
$this['permissions_list'],
$this['groups_list']);
}
}
But If I add the widget/validator to the labSupportForm and save, the firstname value doesn't save.
Am I doing something wrong here, as I would have thought this value would save.
Thanks
When you render a form by fields, you have to explicitly call $form->renderHiddenFields(). For example:
<?php echo form_tag_for($form, '#url') ?>
<table>
<tfoot>
<tr>
<td colspan="2">
<input type="submit" value="Save" />
<?php echo $form->renderHiddenFields() ?>
</td>
</tr>
</tfoot>
<tbody>
<?php echo $form['username']->renderRow() ?>
<?php echo $form['profile_form']->renderRow() ?>
</tbody>
</table>
</form>
Also, beware calling embedded form name the same as relation name (e.g. 'profile') or you will have troubles when saving it. Just add '_form' suffix and you will be safe:
$this->embedForm('profile_form', $form);
If you want to keep a linear display structure of your form fields, you should render them explicitly according to your widget schema:
<?php echo $form['username']->renderRow() ?>
<?php echo $form['profile_form']['first_name']->renderRow() ?>
<?php echo $form['profile_form']['last_name']->renderRow() ?>
Or you can do it automatically for all fields of an embedded form:
<?php foreach ($form['profile_form'] as $field): ?>
<?php if (!$field->isHidden()): ?>
<?php echo $field->renderRow() ?>
<?php endif; ?>
<?php endforeach; ?>
Call $this->saveEmbeddedForms() in the labSupportForm save method
Hi I have two forms, a Specification form and a Source form.
I'm merging the two forms into one so that users can submit a specification and the source of the specification at the same time.
The problem is that the specification table has a field called name and the source table has a field called name. So, when creating the forms and merging, I have two name fields that should refer to two different things, the specification name and the source name. Any way to get around this without restructuring the model/database?
class NewsLinkForm extends BaseNewsLinkForm
{
public function configure()
{
unset($this['id']);
$link = new SourceForm();
$this->mergeForm($link);
$this->useFields(array('name', 'source_url'));
$this->setValidators(array(
'source_url' => new sfValidatorUrl(),
));
$this->validatorSchema->setOption('allow_extra_fields', true);
}
}
class SourceForm extends BaseLimelightForm
{
public function configure()
{
$this->useFields(array('name'));
$this->setWidgets(array(
'name' => new sfWidgetFormInputText(array(),
array(
'class' => 'source_name rnd_3',
'maxlength' => 50,
'data-searchahead' => url_for('populate_sources_ac'),
'data-searchloaded' => '0'
)),
));
$this->setValidators(array(
'name' => new sfValidatorString(array('trim' => true, 'required' => true, 'min_length' => 3, 'max_length' => 50)),
));
$this->widgetSchema->setNameFormat('source[%s]');
}
}
<h5>add specification</h5>
<div class="item">
<?php echo $specificationForm['name']->renderLabel() ?>
<?php echo $specificationForm['name']->render(array('data-searchahead' => url_for('populate_lime_specifications_ac'), 'data-searchloaded' => '0')) ?>
</div>
<div class="item">
<?php echo $specificationForm['content']->renderLabel() ?>
<?php echo $specificationForm['content']->render(array('data-searchahead' => url_for('populate_specifications_ac'), 'data-searchloaded' => '0')) ?>
</div>
<div class="clear"></div>
<div class="item">
<?php echo $specificationForm['name']->renderLabel() ?>
<?php echo $specificationForm['name']->render() ?>
</div>
<div class="item">
<?php echo $specificationForm['source_url']->renderLabel() ?>
<?php echo $specificationForm['source_url']->render() ?>
</div>
You could try this piece of code:
// rename the name field of the first form
$sourceForm->setWidget('source_name', $sourceForm->getWidget('name'));
unset($this['name']);
// merge
$newsLinkForm->mergeForm($sourceForm);