I have a plugin that i want to modify functionality of a method within specific class in Magento 2 however am not quite sure on how to access the original object and return the modified data.
Original Method
protected function _initTotals()
{
$source = $this->getSource();
$this->_totals = [];
$this->_totals['subtotal'] = new \Magento\Framework\DataObject(
['code' => 'subtotal', 'value' => $source->getSubtotal(), 'label' => __('Subtotal')]
);
/**
* Add shipping
*/
if (!$source->getIsVirtual() && ((double)$source->getShippingAmount() || $source->getShippingDescription())) {
$this->_totals['shipping'] = new \Magento\Framework\DataObject(
[
'code' => 'shipping',
'field' => 'shipping_amount',
'value' => $this->getSource()->getShippingAmount(),
'label' => __('Shipping & Handling'),
]
);
}
/**
* Add discount
*/
if ((double)$this->getSource()->getDiscountAmount()) {
if ($this->getSource()->getDiscountDescription()) {
$discountLabel = __('Discount (%1)', $source->getDiscountDescription());
} else {
$discountLabel = __('Discount');
}
$this->_totals['discount'] = new \Magento\Framework\DataObject(
[
'code' => 'discount',
'field' => 'discount_amount',
'value' => $source->getDiscountAmount(),
'label' => $discountLabel,
]
);
}
$this->_totals['grand_total'] = new \Magento\Framework\DataObject(
[
'code' => 'grand_total',
'field' => 'grand_total',
'strong' => true,
'value' => $source->getGrandTotal(),
'label' => __('Grand Total'),
]
);
/**
* Base grandtotal
*/
if ($this->getOrder()->isCurrencyDifferent()) {
$this->_totals['base_grandtotal'] = new \Magento\Framework\DataObject(
[
'code' => 'base_grandtotal',
'value' => $this->getOrder()->formatBasePrice($source->getBaseGrandTotal()),
'label' => __('Grand Total to be Charged'),
'is_formated' => true,
]
);
}
return $this;
}
This i have set to have a plugin to modify functionality of method above with di.xml:
<type name="Magento\Sales\Block\Order\Totals">
<plugin disabled="false" name="Harrigo_EverDiscountLabel_Plugin_Magento_Sales_Block_Order_Totals" sortOrder="10" type="Harrigo\EverDiscountLabel\Plugin\Magento\Sales\Block\Order\Totals"/>
</type>
Plugin
class Totals
{
public function after_initTotals(
\Magento\Sales\Block\Order\Totals $subject,
$result
) {
if ((double)$subject->getSource()->getDiscountAmount() != 0 OR $subject->getSource()->getDiscountDescription() != null) {
if ($subject->getSource()->getDiscountDescription()) {
$discountLabel = __('Offer (%1)', $source->getDiscountDescription());
} else {
$discountLabel = __('Offer');
}
$subject->_totals['discount'] = new \Magento\Framework\DataObject(
[
'code' => 'discount',
'field' => 'discount_amount',
'value' => $source->getDiscountAmount(),
'label' => $discountLabel,
]
);
}
return $subject;
}
}
Have used $subject instead of $this within the plugin, this does not work for me however. How do I access the $this object within the plugin to add / overwrite $this->_totals['discount'] and return the updated $this object from within the plugin. I have it working fine with a standard preference but would rather use a plugin if possible.
I think you should check this before implementing above code.
http://devdocs.magento.com/guides/v2.0/extension-dev-guide/plugins.html
As per devdocs for Magento2 protected functions can not be intercepted so We can not use plugins for that.
May be that is causing issue in your case.
Hope this helps!
Related
I'm trying to trigger push notifications and emails in the background of my Lumen API. I created the WarningUser class that creates both queues:
<?php
namespace App\Utils;
use App\Jobs\ProcessNotification;
use App\Mail\AllMail;
use Illuminate\Support\Facades\Mail;
class WarningUser
{
public static function send($user, $message, $url, $data = [])
{
$url = env('APP_URL_FRONT') . $url;
dispatch(new ProcessNotification($user, $data, $message, $url));
$emailData = EmailTexts::texts('pt', $data)[$message];
Mail::to($user->email)->queue(new AllMail($emailData['title'], $emailData['content']));
return true;
}
}
First we have the ProcessNotification job, which connects to Firebase and sends the notification:
<?php
namespace App\Jobs;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\Notification;
use Kreait\Firebase\Messaging\CloudMessage;
class ProcessNotification extends Job
{
protected $user = null;
protected $data = null;
protected $message = null;
protected $url = null;
public function __construct($user, $data, $message, $url)
{
$this->user = $user;
$this->data = $data;
$this->message = $message;
$this->url = $url;
}
public function handle()
{
if (\is_string($this->user->token) and $this->user->token !== '') {
$messaging = (new Factory())
->withServiceAccount(__DIR__.'/../../private-key.json')
->createMessaging();
$notificationData = NotificationTexts::texts('pt', $this->data)[$this->message];
$messageAlert = CloudMessage::withTarget('token', $this->user->token)
->withNotification(Notification::create($notificationData['title'], $notificationData['content']))
->withData([ 'url' => $this->url ]);
$messaging->send($messageAlert);
}
}
}
And finally AllMail that sends a simple email:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class AllMail extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
protected $title;
protected $body;
public function __construct($title, $body)
{
$this->title = $title;
$this->body = $body;
}
public function build()
{
return $this
->subject($this->title)
->view('email')
->with([
'title' => $this->title,
'body' => $this->body
]);
}
}
I've tested both codes without using queues, and they work, but when I put the queue it stops working. The processes are recorded in my database (mongodb):
But the queue is never processed, I tried to execute php artisan queue: work andphp artisan queue: listen, but neither case works.
The queue is never attempted to be processed, nor does it go to the failed_jobs table
My config / queue.php is as follows. And my .env is with QUEUE_CONNECTION = database
<?php
return [
'default' => env('QUEUE_CONNECTION', 'database'),
'connections' => [
'sync' => [
'driver' => 'sync',
],
'database' => [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
'block_for' => 0,
],
'sqs' => [
'driver' => 'sqs',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'),
'queue' => env('SQS_QUEUE', 'your-queue-name'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'redis' => [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'block_for' => null,
],
],
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database'),
'database' => env('DB_CONNECTION', 'mongodb'),
'table' => 'failed_jobs',
],
];
Can someone help me? I no longer know what the error may be.
PS: At no time is a browser or console error displayed
I managed to solve, for Mongo we need to use some more settings:
The connection in queue.php must be:
'connections' => [
'database' => [
'driver' => 'mongodb',
'table' => 'jobs',
'queue' => 'default',
'expire' => 60,
],
...
And we need to register the package provider that works with mongo:
Jenssegers\Mongodb\MongodbQueueServiceProvider::class,
for my module i need to generate a form with helper. I found nothing with my error on the web so... I post again something...
Here my AdminYoutubeHomeController
<?php
class AdminYoutubeHomeController extends ModuleAdminController
{
public function __construct()
{
$this->bootstrap = true;
$this->display = 'view';
parent::__construct();
$this->meta_title = $this->l('Youtube');
if (!$this->module->active) {
Tools::redirectAdmin($this->context->link->getAdminLink('AdminHome'));
}
}
public function renderView()
{
/**
* If values have been submitted in the form, process.
*/
if (((bool)Tools::isSubmit('submitYoutubeHomeModule')) == true) {
$this->postProcess();
}
$this->context->smarty->assign([
'youtube_dir', _PS_MODULE_DIR_.'youtubehome',
'youtube_embeded' => "https://www.youtube.com/embed/",
'youtubeLink' => Configuration::get('YOUTUBEHOME_LINK_VIDEO')
]);
return $this->context->smarty->fetch(_PS_MODULE_DIR_.'youtubehome/views/templates/admin/youtubehome.tpl').$this->renderForm();
}
/**
* Create the form that will be displayed in the configuration of your module.
*/
public function renderForm()
{
$helper = new HelperForm();
$helper->show_toolbar = false;
$helper->table = $this->table;
$helper->module = $this;
$helper->name_controller = $this->module->name;
$helper->default_form_language = $this->context->language->id;
$helper->allow_employee_form_lang = Configuration::get('PS_BO_ALLOW_EMPLOYEE_FORM_LANG', 0);
$helper->identifier = $this->identifier;
$helper->submit_action = 'submitYoutubeHomeModule';
$helper->currentIndex = $this->context->link->getAdminLink('AdminModules', false)
.'&configure='.$this->module->name.'&tab_module='.$this->module->tab.'&module_name='.$this->module->name;
$helper->token = Tools::getAdminTokenLite('AdminModules');
$helper->tpl_vars = array(
'fields_value' => $this->getConfigFormValues(), /* Add values for your inputs */
'languages' => $this->context->controller->getLanguages(),
'id_language' => $this->context->language->id,
);
return $helper->generateForm(array($this->getConfigForm()));
}
/**
* Create the structure of your form.
*/
public function getConfigForm()
{
return array(
'form' => array(
'legend' => array(
'title' => $this->l('Settings'),
'icon' => 'icon-cogs',
),
'input' => array(
array(
'col' => 3,
'type' => 'text',
'prefix' => '<i class="icon icon-youtube-play"></i>',
'desc' => $this->l('Enter your youtube end link'),
'name' => 'YOUTUBEHOME_LINK_VIDEO',
'label' => $this->l('Link'),
),
),
'submit' => array(
'title' => $this->l('Save'),
),
),
);
}
/**
* Set values for the inputs.
*/
public function getConfigFormValues()
{
return array(
'YOUTUBEHOME_LINK_VIDEO' => Configuration::get('YOUTUBEHOME_LINK_VIDEO'),
);
}
/**
* Save form data.
*/
public function postProcess()
{
$form_values = $this->getConfigFormValues();
foreach (array_keys($form_values) as $key) {
Configuration::updateValue($key, Tools::getValue($key));
}
}
}
And here the error
My tpl file is in modules/youtubehome/views/templates/admin/youtubehome.tpl
I don't want to override the default form. Do you think i have doing something wrong ?
EDIT POST
Here it's the error with ps_version
try with :
$this->setTemplate('module:youtubehome/views/templates/admin/youtubehome.tpl');
Regards
I am having trouble with a simple form. I am quite sure that I miss some critical info about this topic...
I created a custom module, I created a custom form
<?php
class modulename_History_Block_Adminhtml_History_Edit_Form extends Mage_Adminhtml_Block_Widget_Form {
protected function _prepareForm()
{
$form = new Varien_Data_Form(array(
'id' => 'edit_form',
'name' => 'edit_form',
'action' => $this->getUrl('*/*/history', array('id' => 'orders_export')),
'method' => 'post',
'enctype' => 'multipart/form-data',
'data' =>'somethingsomethingdarkaside'
));
$this->setForm($form);
$fieldset = $form->addFieldset('Filtrování objednávek', array('legend'=> 'Nastavte filtr pro report objednávek'));
$dateTimeFormatIso = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$fieldset->addField('date_from', 'date', array(
'label' => 'Změna statusu objednávek od:',
'title' => 'Změna statusu objednávek od:',
'time' => true,
'name' => 'filter_date_from',
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'format' => $dateTimeFormatIso,
'required' => true,
));
$fieldset->addField('export_history_order_status_changed', 'button', array(
'label' => 'Exportovat do souboru:',
'value' => 'Export',
'name' => 'export_history_order_status_changed',
'class' => 'form-button',
'onclick' => "setLocation('{$this->getUrl('*/*/export')}')",
));
$form->setUseContainer(true);
return parent::_prepareForm();
}
And then there is a controller. When the button is pressed, it goes to the correct controller to a correct action. However no data in post received:
<?php
class modulename_History_Adminhtml_History_HistoryController extends Mage_Adminhtml_Controller_Action {
protected function _initAction()
{
return $this;
}
/**
* A page with the form, creating the block with it.
*
*/
public function editAction()
{
$this->_title('Historie objednavky')
->loadLayout()
->_setActiveMenu('modulename/historymenu');
$this->_addContent($this->getLayout()->createBlock('modulename_history/adminhtml_history_edit'));
$this->renderLayout();
}
/**
* A main entrance - when the filter is set and the "export" button pressed then this is the function which starts.
*
* #return bool|Mage_Core_Controller_Varien_Action - either we return a downloadable file or we return false.
*/
public function exportAction()
{
if ($this->_setParameters())
{
if ($this->_setOrdersIds())
{
return $this->_getDownloadFile();
}
}
return false;
}
/**
* Try to get parameters from the admin form. If all correct then we return true. If there is something not set
* then we are unable to continue and we return false.
*
* #return bool - either we were successful with getting the parameters or not.
*/
protected function _setParameters()
{
$parameters = $this->getRequest()->getParams();
$pokus = $this->getRequest()->getPost();
$necf = $this->getRequest()->getPost('edit_form');
$neco = Mage::app()->getRequest()->getParam('edit_form');
}
}
Hellou,
so a competent colleque find an answer within minutes. I mean it was as silly as expected, just remove on click action on button and set the heading from the button to the form. And changed the button to submit. I quess the guy I copied the code from used some other spells of high magic so it worked for him. I hope this will help. Correct form below:
<?php
class modulename_History_Block_Adminhtml_History_Edit_Form extends
Mage_Adminhtml_Block_Widget_Form {
protected function _prepareForm()
{
$form = new Varien_Data_Form(array(
'id' => 'edit_form',
'name' => 'edit_form',
//'action' => $this->getUrl('*/*/history', array('id' => 'orders_export')),
'action' => $this->getUrl('*/*/export', array('id' => 'orders_export')),
'method' => 'post',
'enctype' => 'multipart/form-data',
'data' =>'somethingsomethingdarkaside'
));
$this->setForm($form);
$fieldset = $form->addFieldset('Filtrování objednávek', array('legend'=> 'Nastavte filtr pro report objednávek'));
$dateTimeFormatIso = Mage::app()->getLocale()->getDateTimeFormat(Mage_Core_Model_Locale::FORMAT_TYPE_SHORT);
$fieldset->addField('date_from', 'date', array(
'label' => 'Změna statusu objednávek od:',
'title' => 'Změna statusu objednávek od:',
'time' => true,
'name' => 'filter_date_from',
'image' => $this->getSkinUrl('images/grid-cal.gif'),
'format' => $dateTimeFormatIso,
'required' => true,
));
$fieldset->addField('export_history_order_status_changed', 'submit', array(
'label' => 'Exportovat do souboru:',
'value' => 'Export',
'name' => 'export_history_order_status_changed',
'class' => 'form-button',
//'onclick' => "setLocation('{$this->getUrl('*/*/export')}')",
));
$form->setUseContainer(true);
return parent::_prepareForm();
}
I'm having hard time with a weird behaviour of fileinput.
This is my form:
namespace Frontend\Form;
use NW\Form\Form;
use Zend\InputFilter;
use Zend\Form\Element;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
class EnrollStructure extends Form implements ServiceManagerAwareInterface
{
protected $sm;
public function __construct($name=null) {
parent::__construct("frmEnrollStructure");
$this->setAttribute("action", "/registrazione_struttura/submit")
->setAttribute('method', 'post')
->setAttribute("id", "iscrizione_struttura")
->setAttribute("class", "form fullpage");
$this->addInputFilter();
}
public function init()
{
$structureFs = $this->sm->get('Structure\Form\Fieldsets\Structure');
$structureFs->setUseAsBaseFieldset(true);
$structureFs->remove("id")
->remove("creationTime")
->remove("latLon");
$file = new Element\File("images");
$file->setAttribute('multiple', true);
$this->add($structureFs)->add($file);
$this->add(array(
'name' => 'submit',
'attributes' => array(
'type' => 'submit',
'value' => 'Iscriviti',
'id' => 'sbmtEnrollStructure',
'class' => 'submit_btn'
),
));
$this->setValidationGroup(
array(
'structure' =>
array(
'companyname',
'vatNumber',
'addressStreet',
'addressZip',
'addressCity',
'addressRegion',
'fax',
'publicPhone',
'publicEmail',
'website',
'status',
'ownerNotes',
'category',
'subcategory',
"facilities",
"agreeOnPolicy",
"agreeOnPrivacy",
"subscribeNewsletter",
"contact" => array("name", "surname", "email", "role", "phone"),
),
"images"
));
}
/**
* Set service manager
*
* #param ServiceManager $serviceManager
*/
public function setServiceManager(ServiceManager $serviceManager)
{
$this->sm = $serviceManager;
}
public function addInputFilter()
{
$inputFilter = new InputFilter\InputFilter();
// File Input
$fileInput = new InputFilter\FileInput('images');
$fileInput->setRequired(true);
$fileInput->getValidatorChain()
->attachByName('filesize', array('max' => "2MB"))
->attachByName('filemimetype', array('mimeType' => 'image/png,image/x-png,image/jpg,image/jpeg'))
->attachByName('fileimagesize', array('maxWidth' => 2048, 'maxHeight' => 2048));
$inputFilter->add($fileInput);
$this->setInputFilter($inputFilter);
}
}
Basically, I mainly use a fieldset which contains most of the data I request to the user, plus a File input field.
This is the Fieldset Structure: (most important parts..)
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;
use Zend\Validator\Identical;
use Zend\Validator\NotEmpty;
use Zend\Validator\Regex;
use Zend\Validator\StringLength;
class Structure extends Fieldset implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
protected $sm;
public function __construct()
{
parent::__construct('structure');
}
public function init()
{
$this->setHydrator(new DoctrineHydrator($this->_entityManager(),'Structure\Entity\Structure'));
$this->setObject($this->sm->getServiceLocator()->get("Structure_Structure"));
$id = new Element\Hidden("id");
$name = new Element\Text("companyname");
$name->setLabel("Ragione Sociale");
...........
}
public function getInputFilterSpecification()
{
return array
(
"id" => array(
"required" => false,
),
"companyname" => array(
"required" => true,
"validators" => array(
array('name' => "NotEmpty", 'options' => array("messages" => array( NotEmpty::IS_EMPTY => "Inserire la ragione sociale")))
),
),
.....
}
}
This is my controller:
public function submitAction()
{
try {
$this->layout("layout/json");
$form = $this->getForm('Frontend\Form\EnrollStructure');
//$form->addInputFilter();
$structure = $this->getServiceLocator()->get("Structure_Structure");
$viewModel = new ViewModel();
$request = $this->getRequest();
if ($request->isPost())
{
$post = array_merge_recursive
(
$request->getPost()->toArray(),
$request->getFiles()->toArray()
);
$form->setData($post);
if ($form->isValid())
{
$structure = $form->getObject();
$contact = $structure->getContact();
$this->getServiceLocator()->get('Structure_ContactService')->save($contact);
$files = $request->getFiles()->toArray();
if(isset($files['images']))
{
$count = 3;
foreach($files['images'] as $pos => $file)
{
$fpath = $this->getServiceLocator()->get('RdnUpload\Container')->upload($file);
if(!empty($fpath))
{
if(--$count ==0) break;
$asset = $this->getServiceLocator()->get("Application_AssetService")->fromDisk($fpath, $file['name']);
$this->getServiceLocator()->get("Application_AssetService")->save($asset);
$structure->addImage($asset);
}
}
}
$this->getServiceLocator()->get('Structure_StructureService')->save($structure);
$retCode = RetCode::success(array("iscrizione_struttura!" => array("form_submit_successfull")), true);
}
else
{
$messages = $form->getMessages();
if(empty($messages))
$retCode = RetCode::error(array("iscrizione_struttura" => array("need_at_least_one_file" => "missing file")), true);
else
$retCode = RetCode::error(array("iscrizione_struttura" => $messages), true);
}
$viewModel->setVariable("retcode", $retCode);
return $viewModel;
}
} catch(Exception $e)
{
throw $e;
}
}
The strange thing is that if i remove from the field "images" the "multiple" attribute everything works fine, causing the form not to validate and i get this message:
[images] => Array
(
[fileUploadFileErrorFileNotFound] => File was not found
)
While, if i set the attribute multiple, and the user does not upload a file i get no error, but the form gets invalidated (this is the reason for this "bad" code in my controller:)
$messages = $form->getMessages();
if(empty($messages))
$retCode = RetCode::error(array("iscrizione_struttura" => array("need_at_least_one_file" => "missing file")), true);
else
$retCode = RetCode::error(array("iscrizione_struttura" => $messages), true);
I found the problem was caused by the Jquery form plugin, without it it works fine. :( In case somebody needs, I think the correct action code can be found here (I haven't tryied it anyway)
https://github.com/cgmartin/ZF2FileUploadExamples/blob/master/src/ZF2FileUploadExamples/Controller/ProgressExamples.php
Im having a problem with the addAction in my CRUD application. On the controller the logic does not pass the $form->isValid() verification but the form does not show any error message.
I tried with this (Thanks Sam):
foreach($form->get('product')->getElements() as $el)
{
echo $el->getName()." = ".$el->getValue()." > ".$el->getMessages()." <br/>";
}
That only show the name and value of the field, but not the error message.
I've tried letting the form totally blank and it fire "Value is required and can't be empty" error messages, but then, i fill each field one by one until i don't get more error messages but the form still invalid.
My form has a Product Fieldset as a base fieldset and a submit button. Inside Product Fieldset i have an ID field, a name field, a price field and a Brand Fieldset. Inside my Brand Fieldset i have a id field. Like this:
ProductForm:
class ProductForm extends Form
{
public function init()
{
// we want to ignore the name passed
parent::__construct('product');
$this->setName('product');
$this->setAttribute('method', 'post');
$this->add(array(
'name' => 'product',
'type' => 'Administrador\Form\ProductFieldset',
'options' => array(
'use_as_base_fieldset' => true
),
));
$this->add(array(
'name' => 'submit',
'type' => 'Submit',
'attributes' => array(
'value' => 'Add',
'id' => 'submitbutton',
),
));
}
}
ProductFieldset:
class ProductFieldset extends Fieldset implements ServiceLocatorAwareInterface
{
protected $serviceLocator;
function __construct($name = null)
{
parent::__construct('product_fieldset');
$this->setHydrator(new ArraySerializableHydrator());
$this->setObject(new Product());
}
public function init()
{
$this->add(array(
'name' => 'id',
'type' => 'Hidden',
));
$this->add(array(
'name' => 'name',
'type' => 'Text',
'options' => array(
'label' => 'Name',
),
));
$this->add(array(
'name' => 'price',
'type' => 'Text',
'options' => array(
'label' => 'Price',
),
));
$this->add(array(
'name' => 'brand',
'type' => 'BrandFieldset',
));
}
public function setServiceLocator(ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
BrandFieldset:
class BrandFieldset extends Fieldset
{
function __construct(BrandTable $brandTable)
{
parent::__construct('brand_fieldset');
//$this->setHydrator(new ClassMethodsHydrator(false))->setObject(new Brand());
$this->setHydrator(new ArraySerializableHydrator());
$this->setObject(new Brand());
$brandSelectOptionsArray = $brandTable->populateSelectBrand();
$this->add(array(
'name' => 'id',
'type' => 'Select',
'options' => array(
'label' => 'Brand',
'empty_option' => 'Please select a brand',
'value_options' => $brandSelectOptionsArray,
),
));
}
}
This is my new Form statement in the addAction:
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
Inside my model 'Product' i have the inputFilters, required filter for 'id' field, required filter for 'name' field. And for the Brand field i created other inputFilter and added it to the main inputFilter:
$brandFilter->add($factory->createInput(array(
'name' => 'id',
'required' => true,
'filters' => array(
array('name' => 'Int'),
),
)));
$inputFilter->add($brandFilter, 'brand');
The weird behavior is that my editAction works fine and has the same logic.
Is it there any form of echoing an internal error message from the form, something that helps me to understand WHY the form is not valid.
EDIT 2013-06-01
Here is my full Controller:
class ProductController extends AbstractActionController
{
protected $productTable;
protected $brandTable;
public function indexAction()
{
return new ViewModel(array(
'products' => $this->getProductTable()->fetchAll(),
));
}
public function addAction()
{
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if ($request->isPost()) {
$product = new Product();
$product->brand = new Brand();
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$product->exchangeArray($form->getData());
$this->getProductTable()->saveProduct($product);
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return new ViewModel(array(
'form' => $form,
));
}
public function editAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('product', array(
'action' => 'add'
));
}
// Get the Product with the specified id. An exception is thrown
// if it cannot be found, in which case go to the index page.
try {
$product = $this->getProductTable()->getProduct($id);
}
catch (\Exception $ex) {
return $this->redirect()->toRoute('product', array(
'action' => 'index'
));
}
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$brand = $this->getBrandTable()->getBrand($product->brand);
$product->brand = $brand;
$form->bind($product);
$form->get('submit')->setAttribute('value', 'Edit');
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$this->getProductTable()->saveProduct($form->getData());
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return array(
'id' => $id,
'form' => $form,
);
}
public function deleteAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
if (!$id) {
return $this->redirect()->toRoute('product');
}
$request = $this->getRequest();
if ($request->isPost()) {
$del = $request->getPost('del', 'No');
if ($del == 'Yes') {
$id = (int) $request->getPost('id');
$this->getProductTable()->deleteProduct($id);
}
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
return array(
'id' => $id,
'product' => $this->getProductTable()->getProduct($id)
);
}
public function getProductTable()
{
if (!$this->productTable) {
$sm = $this->getServiceLocator();
$this->productTable = $sm->get('Administrador\Model\ProductTable');
}
return $this->productTable;
}
public function getBrandTable()
{
if (!$this->brandTable) {
$sm = $this->getServiceLocator();
$this->brandTable = $sm->get('Administrador\Model\BrandTable');
}
return $this->brandTable;
}
}
My case was I passed wrong input filter. isValid returns false, but $form->getMessages() is empty. Form OrderForm had the following:
$form->setInputFilter(new \Application\Form\UserInputFilter($er));
When I changed UserInputFilter to OrderInputFilter it works.
Well, I got the answer :D
This is how the addAction should be:
public function addAction()
{
$formManager = $this->serviceLocator->get('FormElementManager');
$form = $formManager->get('Administrador\Form\ProductForm');
$form->get('submit')->setValue('Add');
$product = new Product();
$product->brand = new Brand();
$form->bind($product); // I need to bind the product to the form to pass the isValid() validation
$request = $this->getRequest();
if ($request->isPost()) {
$form->setInputFilter($product->getInputFilter());
$form->setData($request->getPost());
if ($form->isValid()) {
$product = $form->getData();
$this->getProductTable()->saveProduct($product);
// Redirect to list of products
return $this->redirect()->toRoute('product');
}
}
return new ViewModel(array(
'form' => $form,
));
}
Apparently i needed to bind and empty product object to the form to be able to pass the isValid() validation. After that i retrieve a product object from the $form->getData().
You can also do: $form->setBindOnValidate(false);