I am trying to retrieve data (a dependency) from a database and use the returned values to populate a zend form select element.
The final values should look like below:
$this->add(array(
'type' => 'Zend\Form\Element\Select',
'name' => 'jobId',
'options' => array(
'label' => 'countryList',
'value_options' => array(
'1'=>'USA',
'2'=> 'United Kingdom',
etc
'
),
),
'attributes' => array(
'value' => '1' //set selected to '1'
)
));
I have used doctrine2 to retrieve the values, i.e:
public function getOptionsForSelect()
{
$entity = $this->getEntityManager()
->getRepository('Workers\Entity\CountryList')
->findAll();
foreach ($entity as $entity)
{
echo $entity->country;
echo $entity->id;
}
}
The above gives me all the required values. I am however stuck on how to then place these values into an array such that once the $this->getOptionsForSelect() is placed in the form, it will immediately populate the values;
i.e.
foreach ($entity as $entity)
{
$id = $entity->id;
$country= $entity->country;
$data['data'] = $id.'=>'.$country;
}
return $data;
The final version of the form field will look like below:
$this->add(array(
'name' => 'countrylist',
'type' => 'Zend\Form\Element\Select',
'options' => array(
'label' => 'countrylist',
'value_options' => $this->getOptionsForSelect(),
'empty_option' => '--- please choose ---'
)
));
There is ObjectSelect/EntitySelect in DoctrineModule for ZF2 which might simplify this - https://github.com/doctrine/DoctrineModule/blob/master/docs/form-element.md
You don't need your own getOptionsForSelect(), because doctrine can handle that already.
If I understood you correctly, you could implement your getOptionsForSelect like so:
public function getOptionsForSelect()
{
$entity = $this->getEntityManager()
->getRepository('Workers\Entity\CountryList')
->findAll();
$options = array();
foreach ($entity as $entity) {
$options[$entity->id] = $entity->country;
}
return $options;
}
I haven't worked with doctrine, but I'd expect it to have some method to extract data as an array, or at least that I'd be possible to pass it to some serialized object to do that.
Related
I am a Magento beginner so please bear with me...
I am creating a simple extension for my site to add a custom field to my Tags in adminhtml. The custom field is just a number which I need to identify a specific Z-block (cms block extension) so that I can access it as a widget and show it on the frontend in the Tag "category".
I have created a custom module which is working: I set a field in the form using $fieldset and have extended TagController.php, both of which are being used (I made a simple trial to see whether or not they had been recognized). However, I do not know how to go about saving my custom field to DB (whether amending saveAction is enough, and I haven't done it properly, or if I need to add a custom Model or sql install).
Sorry for the "basic" question but I'm new at this, and have mostly done frontend dev (so my extension knowledge is simply limited).
Thank you to anyone who can help...
Claudia
NEW TAG FORM:
public function __construct()
{
parent::__construct();
$this->setId('tag_form');
$this->setTitle(Mage::helper('tag')->__('Block Information'));
}
/**
* Prepare form
*
* #return Mage_Adminhtml_Block_Widget_Form
*/
protected function _prepareForm()
{
$model = Mage::registry('tag_tag');
$form = new Varien_Data_Form(
array('id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post')
);
$fieldset = $form->addFieldset('base_fieldset',
array('legend'=>Mage::helper('tag')->__('General Information')));
if ($model->getTagId()) {
$fieldset->addField('tag_id', 'hidden', array(
'name' => 'tag_id',
));
}
$fieldset->addField('form_key', 'hidden', array(
'name' => 'form_key',
'value' => Mage::getSingleton('core/session')->getFormKey(),
));
$fieldset->addField('store_id', 'hidden', array(
'name' => 'store_id',
'value' => (int)$this->getRequest()->getParam('store')
));
$fieldset->addField('name', 'text', array(
'name' => 'tag_name',
'label' => Mage::helper('tag')->__('Tag Name'),
'title' => Mage::helper('tag')->__('Tag Name'),
'required' => true,
'after_element_html' => ' ' . Mage::helper('adminhtml')->__('[GLOBAL]'),
));
$fieldset->addField('zblock', 'text', array(
'name' => 'zblock_id',
'label' => Mage::helper('tag')->__('Z-Block Id'),
'title' => Mage::helper('tag')->__('Z-Block Id'),
'required' => true,
'after_element_html' => ' ' . Mage::helper('adminhtml')->__('[GLOBAL]'),
));
$fieldset->addField('status', 'select', array(
'label' => Mage::helper('tag')->__('Status'),
'title' => Mage::helper('tag')->__('Status'),
'name' => 'tag_status',
'required' => true,
'options' => array(
Mage_Tag_Model_Tag::STATUS_DISABLED => Mage::helper('tag')->__('Disabled'),
Mage_Tag_Model_Tag::STATUS_PENDING => Mage::helper('tag')->__('Pending'),
Mage_Tag_Model_Tag::STATUS_APPROVED => Mage::helper('tag')->__('Approved'),
),
'after_element_html' => ' ' . Mage::helper('adminhtml')->__('[GLOBAL]'),
));
$fieldset->addField('base_popularity', 'text', array(
'name' => 'base_popularity',
'label' => Mage::helper('tag')->__('Base Popularity'),
'title' => Mage::helper('tag')->__('Base Popularity'),
'after_element_html' => ' ' . Mage::helper('tag')->__('[STORE VIEW]'),
));
if (!$model->getId() && !Mage::getSingleton('adminhtml/session')->getTagData() ) {
$model->setStatus(Mage_Tag_Model_Tag::STATUS_APPROVED);
}
if ( Mage::getSingleton('adminhtml/session')->getTagData() ) {
$form->addValues(Mage::getSingleton('adminhtml/session')->getTagData());
Mage::getSingleton('adminhtml/session')->setTagData(null);
} else {
$form->addValues($model->getData());
}
$this->setForm($form);
return parent::_prepareForm();
}
NEW CONTROLLER:
public function saveAction()
{
if ($postData = $this->getRequest()->getPost()) {
if (isset($postData['tag_id'])) {
$data['tag_id'] = $postData['tag_id'];
}
$data['name'] = trim($postData['tag_name']);
$data['zblock'] = $postData['zblock_id'];
$data['status'] = $postData['tag_status'];
$data['base_popularity'] = (isset($postData['base_popularity'])) ? $postData['base_popularity'] : 0;
$data['store'] = $postData['store_id'];
if (!$model = $this->_initTag()) {
Mage::getSingleton('adminhtml/session')->addError(Mage::helper('adminhtml')->__('Wrong tag was specified.'));
return $this->_redirect('*/*/index', array('store' => $data['store']));
}
$model->addData($data);
if (isset($postData['tag_assigned_products'])) {
$productIds = Mage::helper('adminhtml/js')->decodeGridSerializedInput(
$postData['tag_assigned_products']
);
$model->setData('tag_assigned_products', $productIds);
}
try {
$model->save();
Mage::getSingleton('adminhtml/session')->addSuccess(Mage::helper('adminhtml')->__('The tag has been saved.'));
Mage::getSingleton('adminhtml/session')->setTagData(false);
if (($continue = $this->getRequest()->getParam('continue'))) {
return $this->_redirect('*/tag/edit', array('tag_id' => $model->getId(), 'store' => $model->getStoreId(), 'ret' => $continue));
} else {
return $this->_redirect('*/tag/' . $this->getRequest()->getParam('ret', 'index'));
}
} catch (Exception $e) {
Mage::getSingleton('adminhtml/session')->addError($e->getMessage());
Mage::getSingleton('adminhtml/session')->setTagData($data);
return $this->_redirect('*/*/edit', array('tag_id' => $model->getId(), 'store' => $model->getStoreId()));
}
}
return $this->_redirect('*/tag/index', array('_current' => true));
}
The custom field I'm trying to add is "zblock"...thanks and, again, bear with me! :)
First add the field in database table.
For example if you want to add in your custom table.
ALTER TABLE myCustomModuleTable ADD COLUMN 'myCustomField' int(10);
Thenafter, In your controller action take the model object of that table and set the field.
If you are adding data in existing table row:
$value = 6;
$rowInWhichIWantToSave = Mage:getModel('companyname/modulename')->load($rowId);
$rowInWhichIWantToSave->setData('myCustomField',$value)->save();
If you are adding a new row:
$value = 6;
$rowInWhichIWantToSave = Mage:getModel('companyname/modulename');
$rowInWhichIWantToSave->setData('myCustomField',$value)->save();
Hope this helps!!
How can I custom values with DoctrineModule\Form\Element\ObjectMultiCheckbox?
I used Zend\Form\Element\MultiCheckbox and I set values like this:
$this->add(array(
'type' => 'Zend\Form\Element\MultiCheckbox',
'name' => 'countries',
'options' => array(
'label' => 'Select countries',
'value_options' => array(
'value' => 1,
'label' => 'United Kingdom',
'continent' => 'Europe'
)
)
))
But now I need to use Doctrine 2 Multicheckbox and I need to set custom value options. How can i do this?
I have currently only this:
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectMultiCheckbox',
'name' => 'countries',
'options' => array(
'object_manager' => $this->em,
'target_class' => 'Module\Entity\Country'
)
));
I need this for custom view render. I want to show countries like this:
Europe
- Sweden
- United Kingdom
- and others...
America
- Canada
- United States
- other countries...
SOLVED!
I created a new form element:
ObjectMultiCheckbox:
namespace Application\Form\Element;
use Zend\Form\Element\MultiCheckbox;
use Zend\Stdlib\ArrayUtils;
class ObjectMultiCheckbox extends MultiCheckbox
{
public function setValue($value)
{
if ($value instanceof \Traversable)
{
$value = ArrayUtils::iteratorToArray($value);
foreach ($value as $key => $row)
{
$values[] = $row->getId();
}
return parent::setValue($values);
}
elseif ($value == null)
{
return parent::setValue(array());
}
elseif (!is_array($value))
{
return parent::setValue((array)$value);
}
}
}
It's not really pretty, but it handle object to the form as DoctrineModule\Form\Element\ObjectMultiCheckbox.
My entity which using this code have always identifier 'id' so I can use static code as like this: $row->getId(); It's ugly, but it works!
I'm trying to validate a field inside a Collection.
The Collection refers to Company Areas and is tied to a Company Fieldset
The validation needs to check that the Area Name doesn't exists for that Company in the Database yet.
I'm trying to do this using a Callback validator within my collection element 'area_name', my problem is that the collection is aware only of its own context, that means all fields associated to the Area but not aware of the Company context, so i can't filter my validator by its Company parent.
Is there a way to access the parent context of a collection? or should i need to initialize my form passing the Company object to the Collection prior validating?
EDIT: I forgot to mention that i'm using Doctrine2 so i'm not sure if it is possible to use the Db_NoRecordExists Validator bundled with ZF2
This is an old question and you might have fixed this already, but I had a similar problem recently.
You can create a function in your area model/service: validateAreaCompanyRelation(area, company)and in your fieldset use the callback to use it:
AreaService class:
add a method to return true or false based on query limited by 1 row.
in my case it was somthing like this:
public function validateAreaCompanyRelation($company, $area)
{
$result = false;
$count = $this->getRepository()
->createQueryBuilder('q')
->select('q')
->innerJoin('q.company', 'c')
->innerJoin('q.area','b')
->where('b.id = :area and c.company = :company')
->setParameter('area',$area)
->setParameter('company',$area)
->setMaxResults( 1 )
->getQuery()
->getArrayResult();
if(count($count) <>1){
$result=true;
}
return $result;
}
Area Field set:
inject AreaService to the field set (pass it to construct in factory)
class AreaFieldset extends Fieldset implements InputFilterProviderInterface
{
private $areaService;
public function __construct(areaServiceEntityService $areaService)
{
$this->areaService = $areaService;
}
public function init()
{
$this->add(
array(
'name' => 'area',
'filters' => array(),
'validators' => array (
array(
'name' => 'Zend\Validator\Callback',
'options' => array(
'messages' => array(
\Zend\Validator\Callback::INVALID_VALUE => 'Your custom error message',
),
'callback' => array($this,'vlidateUniqueRelation'),
),
),
)
)
);
array(
'name' => 'company',
'filters' => array(),
'validators' => array (
array(
'name' => 'Zend\Validator\Callback',
'options' => array(
'messages' => array(
\Zend\Validator\Callback::INVALID_VALUE => 'Your custom error message',,
),
'callback' => array($this,'vlidateUniqueRelation'),
),
),
)
)
);
}
public function vlidateUniqueRelation($value, $context)
{
// $value = value
// $context['xxxx'] = xxxxx value
// Logic to validate goes here
$context["company"]
$context["area"]
return $this->AreaService->validateAreaCompanyRelation($context["company"], $context["Area"]);
}
I created a CGridView in Yii whose rows are read from an XML file. I'm not using any models: only a controller (where I read the file) and a view (where I display the grid). What I cannot create is a filter (one input field per column) in the first row of the grid so to visualize only certain rows. How can I do it?
This is what I have until now:
Controller:
<?php
class TestingController extends Controller {
public function actionIndex() {
$pathToTmpFiles = 'public/tmp';
$xmlResultsFile = simplexml_load_file($pathToTmpFiles.'/test.xml');
$resultData = array();
foreach ($xmlResultsFile->result as $entry) {
$chromosome = $entry->chromosome;
$start = $entry->start;
$end = $entry->end;
$strand = $entry->strand;
$crosslinkScore = $entry->crosslinkScore;
$rank = $entry->rank;
$classification = $entry->classification;
$mutation = $entry->mutation;
$copies = $entry->copies;
array_push($resultData, array('Chromosome'=>$chromosome, \
'Start'=>$start, 'End'=>$end, Strand'=>$strand, \
'Crosslink_Score'=>$crosslinkScore,'Rank'=>$rank, \
'Classification'=>$classification, 'Mutation'=>$mutation, \
'Copies'=>$copies));
}
$this->render('index', array('resultData' => $resultData));
}
}
?>
View:
<?php
$dataProvider = new CArrayDataProvider($resultData, \
array('pagination'=>array('pageSize'=>10,),));
$this->widget('zii.widgets.grid.CGridView', array( 'id' => 'mutationResultsGrid',
'dataProvider' => $dataProvider, 'columns' => array(
array(
'name' => 'Chromosome',
'type' => 'raw',
),
array(
'name' => 'Start',
'type' => 'raw',
),
array(
'name' => 'End',
'type' => 'raw',
),
array(
'name' => 'Strand',
'type' => 'raw',
),
array(
'name' => 'Crosslink_Score',
'type' => 'raw',
),
array(
'name' => 'Rank',
'type' => 'raw',
),
array(
'name' => 'Classification',
'type' => 'raw',
),
array(
'name' => 'Mutation',
'type' => 'raw',
),
array(
'name' => 'Copies',
'type' => 'raw',
),
),
));
?>
Thanks for your help
Ale
file: FiltersForm.php (I put it in components folder)
/**
* Filterform to use filters in combination with CArrayDataProvider and CGridView
*/
class FiltersForm extends CFormModel
{
public $filters = array();
/**
* Override magic getter for filters
*/
public function __get($name)
{
if(!array_key_exists($name, $this->filters))
$this->filters[$name] = null;
return $this->filters[$name];
}
/**
* Filter input array by key value pairs
* #param array $data rawData
* #return array filtered data array
*/
public function filter(array $data)
{
foreach($data AS $rowIndex => $row) {
foreach($this->filters AS $key => $value) {
// unset if filter is set, but doesn't match
if(array_key_exists($key, $row) AND !empty($value)) {
if(stripos($row[$key], $value) === false)
unset($data[$rowIndex]);
}
}
}
return $data;
}
}
In your controller:
...
$filtersForm = new FiltersForm;
if (isset($_GET['FiltersForm'])) {
$filtersForm->filters = $_GET['FiltersForm'];
}
$resultData = $filtersForm->filter($resultData);
$this->render('index', array(
'resultData' => $resultData,
'filtersForm' => $filtersForm
)}//end action
And last what need - add filters to CGridView config array:
...
'dataProvider' => $dataProvider,
'enableSorting' => true,
'filter' => $filtersForm,
...
Why don't you store the information from the xml to a database and use YII ActiveRecord?
In your case you would need a live mechanism to filter the resultset on every filter query. Like with the search() method, when you use YII ActiveRecord models.
So you could use something like array_filter() with callback on your array on every filter call. (Edit: or the mechanism used here with stripos to return the matching "rows": Yii Wiki)
Or, second option, you could make the xml parser dependend on your filter inputs, which does not feel good to me :). The parser would habe to parse on every filter input, which could be a problem with big xml files.
Or, as mentioned, save the information to the database and use standard YII mechanisms.
Assuming you can use the data obtained in the objects in your foreach loop as the filter for that particular column, you could then pass these values through to the view, something like:
<?php
class TestingController extends Controller {
public function actionIndex() {
$pathToTmpFiles = 'public/tmp';
$xmlResultsFile = simplexml_load_file($pathToTmpFiles.'/test.xml');
$resultData = array();
foreach ($xmlResultsFile->result as $entry) {
...
$chromosomeFilter[] = $entry->chromosome;
...
}
$this->render('index', array(
'resultData' => $resultData,
'chromosomeFilter' => $chromosomeFilter,
...
);
}
}
?>
And then use that value in the filter for that column;
...
array(
'name' => 'Chromosome',
'type' => 'raw',
'filter' => $chromosomeFilter,
),
...
I've not tested, and it depends a lot on the structure of your xml and $entry->chromosome, but that might help put you on the right path?
I had the same problem
and what I did was I implement http://www.datatables.net/
And pull the data remotely . I pass the sorting and pagination to javascript .
I create a new object and bind it to a form. The user fills out the form and goes to a preview page. I store the user responses in the session.
The problem crops up when I try to reload the object from the session when the user goes back to edit the form. I get :
Error: the Entities passed to the choice field must be managed.
Anyone have an idea of where i might be going wrong? Heres the code for the controllers.
public function previewdealAction(Request $request){
$session = $this->getRequest()->getSession();
$coupon = $session->get('coupon');
$form = $this->createForm(new CouponType(), $coupon);
if ($request->getMethod() == 'POST') {
//bind the posted form values
$form->bindRequest($request);
//once a valid form is submitted ...
if ($form->isValid()){
//Proceed to Previewing deal
$file = $coupon->getImage();
$file->upload();
$session->set('coupon', $coupon);
$repository = $this->getDoctrine()
->getRepository('FrontendUserBundle:Coupon');
$coupons = $repository->findAll();
return $this->render('FrontendHomeBundle:Merchant:dealpreview.html.twig', array('coupon'=>$coupon, 'coupons'=>$coupons));
}
}
}
public function builddealAction(Request $request){
$em = $this->get('doctrine')->getEntityManager();
$user = $this->container->get('security.context')->getToken()->getUser();
//check for a coupon session variable
$session = $this->getRequest()->getSession();
$coupon = $session->get('coupon');
//If coupon is not set
if($coupon == NULL){
$coupon = new Coupon();
$date = new \DateTime(date("Y-m-d H:i:s"));
$coupon->setStartdate($date);
$coupon->setPosterid($user);
$session->set('coupon', $coupon);
}
$form = $this->createForm(new CouponType(), $coupon);
return $this->render('FrontendHomeBundle:Merchant:builddeal.html.twig', array(
'form' => $form->createView(),
));
}
--
namespace Frontend\HomeBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class CouponType extends AbstractType {
public function buildForm(FormBuilder $builder, array $options) {
$builder->add('couponname', 'text');
$builder->add('description', 'textarea');
$builder->add('price', 'money', array('currency' => 'USD'));
$builder->add('originalprice', 'money', array('currency' => 'USD'));
$builder->add('maxlimit', 'integer');
$builder->add('maxper', 'integer');
$builder->add('startdate', 'date', array(
'years' => array(2011, 2012, 2013, 2014),
));
$builder->add('duration', 'choice', array(
'choices' => array(
'3' => 3,
'7' => 7,
'14' => 14,
'30' => 30,
'60' => 60,
'90' => 90,
),
'expanded' => false,
'multiple' => false,
));
$builder->add('expirationdate', 'choice', array(
'choices' => array(
'30' => 30,
'60' => 60,
'90' => 90,
'180' => 180,
),
'expanded' => false,
'multiple' => false,
));
$builder->add('tip', 'integer');
$builder->add('salestax', 'choice', array(
'choices' => array(
'included' => 'Sales tax is included and will be remitted BY YOU at the appropriate tax jurisdiction',
'exempt' => 'Sales tax is exempt according to seller\'s tax jurisdiction',
'collected' => 'Sales tax will be collected BY YOU at time of deal redemption',
),
'expanded' => true,
'multiple' => false,
));
$builder->add('signature', 'text');
$builder->add('city', 'entity', array(
'class' => 'Frontend\\UserBundle\\Entity\\Cities',
'expanded' => false,
'multiple' => false,
));
$builder->add('category', 'entity', array(
'class' => 'Frontend\\UserBundle\\Entity\\Category',
'expanded' => false,
'multiple' => false,
));
$builder->add('address', new AddressType());
$builder->add('image', new DocumentType());
$builder->add('maxper', 'choice', array(
'choices' => array(
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
'10' => 10,
),
'expanded' => false,
'multiple' => false,
));
}
public function getDefaultOptions(array $options) {
return array(
'data_class' => 'Frontend\UserBundle\Entity\Coupon',
);
}
public function getName()
{
return 'user';
}
}
heres the coupon type class
I was experiencing the same problem - I was retrieving data from a form using getData() and storing in the session. Later on, after a redirect, I was attempting to repopulate another instance of the same form using setData().
I experienced no issues with native fields. However, when my form included an entity I was receiving the same dreaded message "Entities passed to the choice field must be managed".
After some head scratching the issue revealed itself to be pretty straightforward (aren't they all?). After a redirect, the entity has become detached; the solution is simply to re-include the entity into the EntityManager using EntityManager::merge(), thus reinstating the entity as a managed object :)
// an array of form data from session
$entity = $data['my_entity'];
// merge() returns the managed entity
$entity = $this->getDoctrine()->getEntityManager()->merge($entity);
// update the form data array
$data['my_entity'] = $entity;
// Create form with form data
$form = $this->createForm(new MyFormType(), $data);
http://www.doctrine-project.org/api/orm/2.0/doctrine/orm/entitymanager.html
Hope this helps!
It's not relating to solve your specific problem but I'd like to annotate:
I've had the same problem and could solve it by removing 'by_reference' => false which was needless here and the reason for this error.
Had the same problem and used pretty much the answer Daggah supposed, but added a small loop through the array of entities, checking for objects:
if ($this->get('session')->has('filters')) {
$filters = $this->get('session')->get('filters');
foreach ($filters as $key => $filter) {
if (is_object($filter)) {
$filters[$key] = $em->merge($filter);
}
}
$filterForm = $this->createForm(new FilterType(), $filters);
}
Hope this helps someone.
I had the same problem and both answers were very helpful, but my issue involved multidimensional arrays so to keep things dynamic I used a recursive version of jahller's function.
private function manageObjects(&$data_array)
{
foreach ($data_array as $key => &$value)
if (is_object($value))
$data_array[$key] = $this->container->get('doctrine.orm.entity_manager')->merge($value);
else if (is_array($value))
$this->manageObjects($value);
}
Hope this helps someone.
More complex solution basing on prev comments.
Support ArrayCollections and DateTime objects
/**
* Merge objects
* Allow to manage object by doctrine when using stored (eg. in session data values)
* #param $data_array - list of form fields
* #return mixed
*/
public function manageObjects($data_array)
{
foreach ($data_array as $key => $value) {
// for multi choices
if ($value instanceof ArrayCollection) {
$data_array[$key] = $this->manageObjects($value);
}
//ommit dateTime object
elseif ($value instanceof \DateTime) {
}
elseif (is_object($value)) {
$data_array[$key] = $this->getService('doctrine.orm.entity_manager')->merge($value);
}
}
return $data_array;
}