How to add a calculated field in the FormEvents::PRE_SUBMIT Event - events

In my listener I need to access my entity when in the FormEvents::PRE_SUBMIT event. In POST_SET_DATA this is no problem, just use $event->getData();.
So for the event listening to POST_SET_DATA I am fine with this code:
public function postSetData(FormEvent $event)
{
$form = $event->getForm();
$day = $event->getData();
$total = $this->dayManager->calculateTotal($day); // Passing a Day object, yay!
$form->get('total')->setData($total);
}
However in my method for the PRE_SUBMIT event.
I need this function because on submitting, total is not calculated with the newly submitted data.
public function preSubmit(FormEvent $event)
{
$form = $event->getForm();
// $day = $event->getData();
// Raw array because $event->getData(); holds the old not updated Day object
$day = $form->getData();
// Ough! Had to create and call a seperate function on my dayManager that handles the raw event array
$total = $this->dayManager->calculateTotalFromArray($event->getData());
// Modify event data
$data = $event->getData();
// Ough(2)! Have to do nasty things to match the raw event array notation
$totalArray = array(
'hour' => $total->format('G') . "",
'minute' => intval($total->format('i')) . ""
);
$data['total'] = $totalArray;
$event->setData($data);
}
As you can see, it works. However this is such a hackish way, I do not believe the pro's do it this way. Two things that go wrong here:
Cannot work with entity Day object in the preSubmit function
Have to create the calculateTotalFromArray function in the dayManager
Ugly code to match the raw event array in the preSubmit function
So the main question: how to get an updated Day object from the form in the PRE_SUBMIT form event.

Use SUBMIT instead of PRE_SUBMIT
Don't worry, the form is not yet submitted, SUBMIT is executed right before Form::submit
Why are you having this problem?
All data in PRE_SUBMIT has not been normalized into your usual object...
If you'd like to learn more about the whole thing, please head to: http://symfony.com/doc/current/components/form/form_events.html

Thanks #galeaspablo for submitting the answer! However I add below my code how I solved my particular problem.
My goal was to show a calculated total field in a form. Nothing more. However in the SUBMIT event you cannot do a $form->get('total')->setData($total);. You will get a warning: You cannot change the data of a submitted form.
So altering a form after PRE_SUBMIT is not possible. But adding fields is..
My complete solution is as follows:
In the DayType formbuilder:
// Will add total field via subscriber
//->add('total', TimeType::class, ['mapped' => false])
In the event subscriber:
class CalculateDayTotalFieldSubscriber implements EventSubscriberInterface
{
private $dayManager;
public function __construct(DayManager $dayManager)
{
$this->dayManager = $dayManager;
}
public static function getSubscribedEvents()
{
return array(
FormEvents::SUBMIT => 'addTotalField',
FormEvents::POST_SET_DATA => 'addTotalField'
);
}
public function addTotalField(FormEvent $event)
{
$form = $event->getForm();
$day = $event->getData();
$total = $this->dayManager->calculateTotal($day);
$form->add('total', TimeType::class, ['mapped' => false, 'data' => $total]);
}
}
Note the use of the save function for both the SUBMIT and the POST_SET_DATA events. A good read was: http://symfony.com/doc/current/components/form/form_events.html

Related

Prestashop 1.7.5 module - Update SQL custom table

I'm trying to add a new module to my shop to manage the sql table's values that I made. I can't find a proper guide that show me how to do that because all the forms have values contained in ps_configuration and not within a custom table.
How can I show those values in my form and get to update them?
Thank you if you'll take the time to answer that ^^
So, my form fields are still blank and they don't update my table when I submit.
I added this to "_construct" function:
public function __construct() {
$this->id_sell = $id_sell;
$this->country = $country;
$this->p_cod = $p_cod;
and this to "getContent"
public function getContent() {
$sqlV = 'SELECT * FROM `'._DB_PREFIX_.'mytable` WHERE id_sell = 1';
if ($row = Db::getInstance()->getRow($sqlV))
$country = $row[country];
$p_cod = $row[p_cod];
and last this on "getConfigFormValues":
protected function getConfigFormValues()
{
return array(
'country' => Tools::getValue('country'),
'p_cod' => Tools::getValue('p_cod'),
);
}
So, now I know that a need a class ObjectModel {}, too. Working on it.. and hoping for the best :D

One-shot laravel validator

I have a form where someone searches for something. Based on this form, I validate if the input is correct:
$validator = Validator::make(Input::all() , array(
'address' =>'required',
));
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
After this, I want to validate something else (that a result object isn't empty), which is completely unrelated to the search. In other words, it's NOT input from a form.
1) Do I create another validator to validate this? Or
2) Is there a better way to simply check this value and spawn an object that can be returned with "withErrors"?
UPDATE
This isn't working for me:
$validator = Validator::make(
array(
'searches' => sizeof($search)
) ,
array(
'searches' => 'required|min:1'
)
);
if($validator->fails()) {
return Redirect::to('/')->withErrors($validator);
}
It's not working because for some reason it's picking up that the "searches" item should only be validated "sometimes"
you have two ways. one is custom validator
or there is a simpler way,
suppose,
private function foo()
{
$data = ''; //retrieved the data error here with whatever call you want to make
return !empty($data) ? true : false;
}
in the controller,
public function bar()
{
if(!$this->foo())
{
$messages = new \Illuminate\Support\MessageBag;
// you should use interface here. i directly made the object call for the sake of simplicity.
$messages->add('custom', 'custom error');
return Redirect::back()->withErrors($messages)->withInput();
}
}
in the view:
#if($errors->has('custom'))
<p>custom error output.</p>
#endif
it is just the outline to give you the idea.

check if dates are not within range before saving in yii

how can i check if the dates posted in user's form exist in a range before saving? It's suppose to check different scenario date ranges. Below is just one of them. If it doesn't fall into range, it then posts.
is there a better way to do this?
$model = Table::model();
$criteria = new CDbCriteria;
//this is where i don't know how to get the values ($start,$end) from user before posting
$criteria->addCondition('start_date < '.$start);
$criteria->addCondition('end_date > '.$end);
You have to create custom validation function :
In rules add : this rules will work for both insert and update.
public function rules()
{
return array(
array('dateField', 'myCheckdate',),
);
}
In your custom validation function , you can apply your code to check the date range
public function myCheckdate($attribute,$params)
{
if(!$this->hasErrors())
{
if(Condition == false) // place your condition here
{
$this->addError($attribute,'The date is incorrect.');
}
}
}

How to approach caching in ZF2

I am just starting to get my head into caching as a whole. I have a simple indexAction() that fetches all given Datasets. My approach is:
check for existing key 'controllername-index-index'
if existing: return the value of the key
if not existing, do the normal action and add the key
The value inside the key should be the ViewModel that will be generated and populated with my data.
Here's what i have done so far:
<?php
public function indexAction()
{
$sl = $this->getServiceLocator();
// $cache = $sl->get('cache');
// $key = 'kennzahlen-index-index';
//
// if ($cache->hasItem($key)) {
// return $cache->getItem($key);
// }
$viewModel = new ViewModel();
$viewModel->setTemplate('kennzahlen/index/index');
$entityService = $sl->get('kennzahlen_referenzwert_service');
$viewModel->setVariable('entities', $entityService->findAll());
// $cache->setItem($key, $viewModel);
return $viewModel;
}
The Caching parts are commented out for testing purposes, but basically this is all that i am doing. The Caching config/service looks like the following:
<?php
'cache' => function () {
return \Zend\Cache\StorageFactory::factory(array(
'adapter' => array(
'name' => 'filesystem',
'options' => array(
'cache_dir' => __DIR__ . '/../../data/cache',
'ttl' => 100
),
),
'plugins' => array(
array(
'name' => 'serializer',
'options' => array(
)
)
)
));
},
The serialization and caching works quite well, but i am surprised by the missing results. Going by what the ZendDevelopersToolbar tells me, the times WITHOUT caching range between 1.8s to 2.5s. Having the caching parts uncommented (enabled) doesn't really improve the loading time of my page at all.
So my question is: Is this approach completely wrong? Are there different, more speedy parts, that can be saved with some neat configuration tricks?
I Feel that a 2 second load time of a page is DEFINITELY too slow. 1s to me is the maximum given a huge amount of data, but certainly not anything more than that :S
All help/hints/suggestions will be greatly appreciated. Thanks in advance!
One option would be to cache the complete output of your page, for example based on the route match. You need to listen between routing and dispatching which route has been found as match and then act accordingly:
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
// A list of routes to be cached
$routes = array('foo/bar', 'foo/baz');
$app = $e->getApplication();
$em = $app->getEventManager();
$sm = $app->getServiceManager();
$em->attach(MvcEvent::EVENT_ROUTE, function($e) use ($sm) {
$route = $e->getRouteMatch()->getMatchedRouteName();
$cache = $sm->get('cache-service');
$key = 'route-cache-' . $route;
if ($cache->hasItem($key)) {
// Handle response
$content = $cache->getItem($key);
$response = $e->getResponse();
$response->setContent($content);
return $response;
}
}, -1000); // Low, then routing has happened
$em->attach(MvcEvent::EVENT_RENDER, function($e) use ($sm, $routes) {
$route = $e->getRouteMatch()->getMatchedRouteName();
if (!in_array($route, $routes)) {
return;
}
$response = $e->getResponse();
$content = $response->getContent();
$cache = $sm->get('cache-service');
$key = 'route-cache-' . $route;
$cache->setItem($key, $content);
}, -1000); // Late, then rendering has happened
}
}
The second listener checks at the render event. If that happens, the result of the response will be cached.
This system (perhaps not with 100% copy/paste, but the concept) works because if you return a Response during the route or dispatch event, the application will short circuit the application flow and stop further triggering listeners. It will then serve this response as it is.
Bear in mind it will be the complete page (including layout). If you don't want that (only the controller), move the logic to the controller. The first event (now route) will be dispatch of the controller. Listen to that early, so the normal execution of the action will be omitted. To cache the result, check the render event for the view layer to listen to.
/update: I wrote a small module to use this DRY in your app: SlmCache

Codeigniter passing 2 arguments to callback

After posting a form having two fields named 'id' and 'url' I have the following code:
$this->load->library('form_validation');
$this->form_validation->set_rules('id', 'id', 'trim|xss_clean');
$this->form_validation->set_rules('url', 'url|id', 'trim|xss_clean|callback_url_check');
A db query needs both fields.
The function url_check($str, $id) is called but in this case 'id' always has the value 0.
If I just do :
$this->form_validation->set_rules('url', 'url', 'trim|xss_clean|callback_url_check');
And call url_check($str) everything's working as it's is supposed to do.
The question is how do I pass two values to the url_check($str, $id)?
You can use $this->input->post directly:
function check_url() {
$url = $this->input->post('url');
$id = $this->input->post('id');
// do some database things you need to do e.g.
if ($url_check = $this->user_model->check_url($url, $id) {
return TRUE;
}
$this->form_validation->set_message('Url check is invalid');
return FALSE;
}
Just do it the right way (at least for CI 2.1+) as described in the docs:
$this->form_validation->set_rules('uri', 'URI', 'callback_check_uri['.$this->input->post('id').']');
// Later:
function check_uri($field, $id){
// your callback code here
}
This seems to work also.
$id = 1;
$this->form_validation->set_rules('username', 'Human Username', 'callback_username_check['.$id.']');
function username_check($str, $id) {
echo $id;
if ($str == 'test') {
$this->form_validation->set_message('username_check', 'The %s field can not be the word "test"');
return FALSE;
}
else {
return TRUE;
}
}
If I understand form_validation correctly, each rule (set_rules) is for one field of the form and your callback will only check the one field. In your case it would seem that 'id' is out of scope. Instead, one can pass an array to the set_rules function and do the callback. I have not tried this yet. http://codeigniter.com/user_guide/libraries/form_validation.html#validationrulesasarray
Just a note on using the callback parameters as suggested in the other answers. If you are using app/config/form_validation.php to create your validation rules, the $this->input->post('parameter') syntax will not work since that object is not available in the CI Loader at the point in the execution where it reads the contents of that file. You would have to do the call in your callback routine e.g. :
public function _validate_user_signup($username, $password) {
$var = $this->input->post('password');
In this case the second parameter passed to the method would not contain the password, but $var would after the call.
I hope that's clear.
Matt
It's better to use Form Validation library to get the data that is being validated.
Not always your data will be in $_GET or $_POST (see https://www.codeigniter.com/userguide3/libraries/form_validation.html#validating-an-array-other-than-post).
The best way you can access your data inside a validation callback is this:
$this->form_validation->validation_data
"validation_data" is a public property in the CI_Form_validation class.

Resources