I'm trying to make some validations in Laravel. I have a resource with nested resources, so I need to validate many things at once. I have a Level, which is composed of many Questions. So In order to validate them, I first validate the Level, and then each question one by one.
I use the following method:
public function ValidateDataAgainstModel($data) {
$book = Level::ValidationBook(); // Or Answer::ValidationBook()
$validator = Validator::make($data,
$book['rules'],
$book['messages']);
if ($validator->fails()) {
return $validator->errors();
}
return null;
}
That method is used in Level and Question, so it is the same.
Then I group all errors in a single array like this:
$errors = [];
$errors['level'] = $levelError;
$errors['questions'] = $questionsErrors; //$questionErrors is an array of each question errors
Then to check that my array is correct I make a \Log::info($errors) and get the following result:
[2018-12-12 23:47:12] local.INFO: array (
'level' =>
Illuminate\Support\MessageBag::__set_state(array(
'messages' =>
array (
'name' =>
array (
0 => 'Se requiere el nombre del nivel',
),
),
'format' => ':message',
)),
'questions' =>
array (
0 =>
Illuminate\Support\MessageBag::__set_state(array(
'messages' =>
array (
'description' =>
array (
0 => 'Se requiere la descripciĆ³n de la pregunta',
),
),
'format' => ':message',
)),
1 =>
Illuminate\Support\MessageBag::__set_state(array(
'messages' =>
array (
'description' =>
array (
0 => 'Se requiere la descripciĆ³n de la pregunta',
),
),
'format' => ':message',
)),
),
)
As you can see I have two questions with errors. In order to return this to my client, when I do the following
if ( count($errors) > 0 ) {
throw ValidationException::withMessages($errors);
}
But when the data arrives at my client, the question errors are not complete. Instead of getting an index with an array of the questions errors, I only got one question error.
Does anyone know why this is happening? It seems like is grouping the errors in a single one.
Finally, I'm adding the entire method that computes the result:
public function Create($data) {
// Create array to handle all the nested resources
$errors = [];
// Validate that the data array is correct
$levelErrors = $this->ValidateDataAgainstModel($data);
if ( !is_null($levelErrors) ) {
$errors['level'] = $levelErrors;
}
$questionsErrors = [];
foreach($data['questions'] as $questionData) {
/*
* We remove the level_id because we are validating the
* question content
*/
$validationBook = Question::ValidationBook();
unset($validationBook['rules']['level_id']);
$questionErrors = QuestionService::ValidateDataAgainstModel($questionData, $validationBook);
if ( !is_null($questionErrors) ) {
$questionsErrors[$questionData['index']] = $questionErrors;
}
}
if ( count($questionsErrors) > 0 ) {
$errors['questions'] = $questionsErrors;
}
if ( count($errors) > 0 ) {
\Log::info($errors);
throw ValidationException::withMessages($errors);
}
// Create the object
$result = Level::create($data);
return $result;
}
I don't know if the problem is the withErrors method implementation that you can see here ValidationException Source Code
Related
im doing a lazy loading with WP_Query Pagination
it's working fine but the content duplicate itself when it reaches it's end
and when i search for a specific result it shows the result correctly
but after that it still want to do lazy load so it load random data
here is my code
lazy-load.php
<?php
add_action('wp_ajax_nopriv_load_posts_by_ajax', 'load_posts_by_ajax_callback');
add_action('wp_ajax_load_posts_by_ajax', 'load_posts_by_ajax_callback');
function load_posts_by_ajax_callback(){
// check_ajax_referer( 'load_more_posts', 'security' );
$paged = $_POST['page'];
$args = array(
'post_type' => 'unit',
'post_status' => 'publish',
'posts_per_page' => 4,
'paged' => $paged
);
if ( !empty($_POST['taxonomy']) && !empty($_POST['term_id']) ){
$args['tax_query'] = array (
array(
'taxonomy' => $_POST['taxonomy'],
'terms' => $_POST['term_id'],
),
);
}
if ( ! is_null($_POST['offer']) ) {
$args['meta_query'][] = array(
'key' => 'WAKEB_hot',
'value' => '1',
'compare' => '=',
);
}
if ( ! is_null($_POST['purpose']) ) {
$args['meta_query'][] = array(
'key' => 'WAKEB_vacation',
'value' => '1',
'compare' => '=',
);
}
if (!empty($_POST['project'])){
$args['meta_query'] = array (
array(
'key' => 'WAKEB_project',
'value' => $_POST['project'],
'compare' => '='
),
);
}
// start buffer
ob_start();
$query = new WP_Query( $args );
if ( $query->have_posts() ) :
while($query->have_posts()){ $query->the_post();
get_template_part("template-parts/units");
}
endif; wp_reset_postdata();
// start buffered data in data variable
$data = ob_get_clean();
wp_send_json_success( $data );
wp_die();
}
add_action('wp_ajax_nopriv_load_projects_by_ajax', 'load_projects_by_ajax_callback');
add_action('wp_ajax_load_projects_by_ajax', 'load_projects_by_ajax_callback');
function load_projects_by_ajax_callback(){
// check_ajax_referer( 'load_more_posts', 'security' );
$paged = $_POST['page'];
$args = array(
'post_type' => 'project',
'post_status' => 'publish',
'posts_per_page' => 4,
'paged' => $paged
);
if ( ! is_null($_POST['ptype']) ) {
$args['tax_query'] = array (
array(
'taxonomy' => 'pptypes',
'field' => 'slug',
'terms' => $_POST['ptype'],
),
);
}
if ( !empty($_POST['taxonomy']) && !empty($_POST['term_id']) ){
$args['tax_query'] = array (
array(
'taxonomy' => $_POST['taxonomy'],
'terms' => $_POST['term_id'],
),
);
}
// start buffer
ob_start();
$query = new WP_Query( $args );
if ( $query->have_posts() ) :
while($query->have_posts()){ $query->the_post();
get_template_part("template-parts/projects");
}
endif; wp_reset_postdata();
// start buffered data in data variable
$data = ob_get_clean();
wp_send_json_success( $data );
wp_die();
}
lazy-load.js
$('.unit-terms li a').each( function() {
if ( this.href == window.location.href ) {
$(this).parent().addClass('current');
}
});
main.js
(function($){
$('.isotope a').on('click', function(){
$('.isotope .active').removeClass('active');
$(this).addClass('active');
var filter = $(this).data('filter');
if(filter=='*'){
$('.property').show();
}else{
$('.property').not(filter).hide();
$('.property'+filter).show();
}
return false;
});
})(jQuery);
so how can i make it work? i don't know what im doing wrong here
Here is the repo link for the full project
https://github.com/Ov3rControl/hoomerz
ok, now I understand what you meant ;) During lazy load you send to backend only page number without current state of filters and / or search string. So it sends all posttype items based on page number only. You should send also current state of filters
main.js: add this to your after-page-load function:
var currentUrl = new URL(window.location.href);
var searchQuery = urlObj.searchParams.get("k");
lazy-load.js: add search param to data posted to backend
var data = {
'action': 'load_posts_by_ajax',
'page': page,
'search: searchQuery // new field
};
lazy-load.php: add search param to WP_Query
if ( isset($_POST['search']) && !empty($_POST['search']) ){ // new section
$args['s'] = sanitize_text_field($_POST['search']);
}
That's example for text search filter. For all filters you must
1. match every filter from front (URL get param) (main.js)
2. than put it in data object sent to backend (lazy-load.js)
3. address this variable in lazy-load.php in if(isset($_POST['param-name'])) section ( new or existing one as there are some )
I suggest to try without ob_start() / ob_get_clean(). Also if you generate html instead of raw data structure, I would simply print it to output without wp_send_json_success().
Other solution would be sending raw data (1. array in php, 2. json_encode(), 3. wp_send_json() ) and than processing in javascript (dynamic dom element creation after request to backend made).
I have a problem where my database column is set to NULL
`max_vol` MEDIUMINT UNSIGNED NULL,
and my $_POST['max_vol[]'] is an empty string ('') right up to the point of inserting or updating the row in the database (tested output with print_r()). It then inserts 0 (zero) instead of NULL.
If I explicitly set max_vol to NULL it then works.
$value['max_vol'] = empty($value['max_vol']) ? NULL : $value['max_vol'];
but why does this happen? I thought setting an empty string to MySQL (with NULL set) inserted NULL. Here is my original code. Is this something CodeIgniter's query builder changes?
$position_form_data = array(); // positions form data store
// process the form data into arrays for database operations
foreach( $_POST as $post_key=>$post_value ) {
// ignore non-array post variables
if( is_array( $post_value ) ) {
foreach( $post_value as $form_key=>$form_value ) {
if (!isset($position_form_data[$form_key])) {
$position_form_data[$form_key] = array();
}
$position_form_data[$form_key][$post_key] = $form_value;
}
}
}
// if id exists db->update else db->insert
foreach($position_form_data as $value){
// $value['max_vol'] = empty($value['max_vol']) ? NULL : $value['max_vol'];
// data for insert and replace db operations
$data = array(
'id' => $value['id'],
'day' => $_POST['day'],
'title' => $value['title'],
'description' => $value['description'],
'max_vol' => $value['max_vol'],
'is_draft' => $is_draft,
'project_id' => $_POST['project_id']
);
//print_r($data);exit();
if( empty($value['id']) ) {
$this->db->insert('positions', $data);
} else {
$this->db->replace('positions', $data);
}
Thanks.
I can't see any mistake, I can only recommend you try this:
if( ! isset($value['max_vol'])){
if( is_null($value['max_vol'])){
$_max_vol = NULL;
}
else{
$_max_vol = NULL
}
}
else{
$_max_vol = $value['max_vol'];
}
$data = array(
'id' => $value['id'],
'day' => $_POST['day'],
'title' => $value['title'],
'description' => $value['description'],
'max_vol' => $_max_vol,
'is_draft' => $is_draft,
'project_id' => $_POST['project_id']
);
I have a Form having primary key on two fields (gid, bid). I need to add validation to block duplicate entries into database.
I have checked with ZF2 Solution for this . http://framework.zend.com/manual/2.2/en/modules/zend.validator.db.html#excluding-records . While this approach of handling composite keys is not look the ideal way, But still I am trying it because it look like only buil-in way. Now it require me to provide second field's value (value option in exclude), which is again a problem. As I am trying it
$inputFilter->add(array(
'name' => 'gid',
'required' => true,
'validators' => array(
array(
'name' => 'NotEmpty',
'options' => array(
'messages' => array(
'isEmpty' => 'required'
),
),
),
array (
'name' => 'Zend\Validator\Db\NoRecordExists',
'options' => array (
'table' => 'gtable',
'field' => 'gid',
'adapter' => $this->dbAdapter,
'messages' => array(
\Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database'
),
'exclude' => array(
'field' => 'bid',
'value' => [?],
),
)
),
)
));
How do I get this value, As Form is absolute separate Class/File than controller where I have the submitted form values. Is some better architecture solution of this problem exists Or Some hack to pass submitted field value to Form Class is only solution ?
Note : I am not in favor of Build My Validation Plugin for this task as short time is constraint for functionality.
You can do all the job in your form. To achieve that, you could define your forms as factories in your module Module.php.
Module.php
use MyNamespace\MyForm;
//NOTE THAT THE SERVICE MANAGER IS INJECTED. YOUR FORM COULD RECEIVE IT THROUGH THE CONSTRUCTOR
public function getServiceConfig()
{
return array(
'factories' => array(
'my_form' => function( $sm ) {
$form = new MyForm( $sm );
return $form;
},
),
);
}
When you want to use the form is as easy as use this code in your controller:
class MyController extends AbstractActionController
{
public function createAction() {
$form = $this->getServiceLocator()->get( 'my_form' ) );
(...)
}
}
And your MyForm.php
use Zend\Form\Form;
class MyForm extends Form
{
public $serviceManager, $request, $postData;
public function __construct( $serviceManager ) {
parent::__construct( null );
$this->serviceManager = $serviceManager;
$this->request = $serviceManager->get( 'Application')->getMvcEvent()->getRequest();
$this->postData = get_object_vars( $this->request->getPost() );
}
}
This way you can get advantage of the Service Manager within your form. And the public postData, where you'll find the bid value you're looking for to build your NoRecordExists filter.
You could add the parameters to the getInputFilter, like this :
getInputFilter($gid, $bid)
And then on the controller, when you set the filter you pass the 2 parameters, and then just check as $form->isValid(); ...
Alternative try this:
array(
'name' => 'Db\NoRecordExists',
'options' => array(
'table' => 'gtable',
'field' => 'gid',
'adapter' => $this->dbAdapter,
),
),
I'm unsure on your use case. If you were to add a database entry the primary keys for that table would not be known until you insert anyway - If you have foreign key constraints you could handle the exception from the database.
I am not in favor of Build My Validation Plugin for this task
The validator is also not designed to validate multiple fields as they are attached to a form element on a 1-1 basis. You will therefore need to create your own.
The below example has NOT been tested, so take it as an example of the approach rather than working code.
The key bit is the isValid method.
namespace MyModule\Validator\Db;
use Zend\Validator\Db\NoRecordExists;
class CompositeNoRecordExists extends NoRecordExists
{
protected $field2;
protected $field2Value;
public function __construct($options = null)
{
parent::__construct($options);
if (array_key_exists('field2', $options)) {
$this->setField2($options['field2']);
} else {
throw new \BadMethodCallException('Missing field2 option!');
}
}
protected function setField2Value(array $context)
{
if (! isset($context[$this->field2])) {
throw new \BadMethodCallException('Unable to find value for field 2');
}
$this->field2Value = $context[$this->field2];
}
public function isValid($value)
{
// The isValid() method is actually given a 2nd argument called $context
// Which is injected by the inputFilter, via the input and into the validator chain
// $context contains all of RAW form element values, keyed by thier element name.
// Unfortunately due to the ValidatorInterface you are unable to add this to the method
// signature. So you will need to be 'creative':
$args = func_get_args();
if (isset($args[1]) && is_array($args[1])) {
$this->setField2Value($args[1]);
} else {
throw new \BadMethodCallException('Missing validator context');
}
return parent::isValid($value);
}
public function getSelect()
{
$select = parent::getSelect();
$select->where->equalTo($this->field2, $this->field2Value);
return $select;
}
}
Then all you would need to do is update the validator config, adding the field2 field name.
array (
'name' => 'MyModule\Validator\Db\CompositeNoRecordExists',
'options' => array (
'table' => 'gtable',
'field' => 'gid',
'field2' => 'bid',
'adapter' => $this->dbAdapter,
'messages' => array(
\Zend\Validator\Db\NoRecordExists::ERROR_RECORD_FOUND => 'The specified key already exists in database'
),
)
),
I am having some issues with a form's CSRF validation and the use of sessions which are stored in the database using ZF2.
Here is the code I have added to Module.php onBootstrap() method:
// create session which is persisted in the database
$dbAdapter = $serviceManager->get('Zend\Db\Adapter\Adapter');
$sessionTableGateway = new TableGateway\TableGateway('XXX.XXX', $dbAdapter);
$sessionOptions = new DbTableGatewayOptions();
$sessionOptions->setDataColumn('SESSION_DATA')
->setIdColumn('SESSION_ID')
->setModifiedColumn('SESSION_MODIFIED')
->setLifetimeColumn('SESSION_LIFETIME')
->setNameColumn('SESSION_NAME');
$sessionGateway = new DbTableGateway($sessionTableGateway, $sessionOptions);
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions(array(
'gc_probability' => 1,
'gc_divisor' => 1,
'use_cookies' => true
));
$storage = new SessionStorage();
$sessionManager = new SessionManager($sessionConfig, $storage);
$sessionManager->setSaveHandler($sessionGateway);
$sessionManager->start(true);
Container::setDefaultManager($sessionManager);
In the form, I am creating a standard CSRF element like this:
$this->add(array(
'name' => 'csrf',
'type' => 'Zend\Form\Element\Csrf'
));
When the form is loaded, I can see the CSRF hash in the session which is stored in the database:
__ZF|a:2:{s:20:"_REQUEST_ACCESS_TIME";d:1383673583.296492099761962890625;s:29:"Zend_Validator_Csrf_salt_csrf";a:1:{s:6:"EXPIRE";i:1383673883;}}FlashMessenger|C:23:"Zend\Stdlib\ArrayObject":21:{x:i:2;a:0:{};m:a:0:{}}Zend_Validator_Csrf_salt_csrf|C:23:"Zend\Stdlib\ArrayObject":72:{x:i:2;a:1:{s:4:"hash";s:32:"1ba5170385f4c2e2839766f19c3c2dbd";};m:a:0:{}
When I submit the form, I do not get any errors, however, it seems that the form's isValid() method is failing, and it looks like the CSRF Validation routine always gets a null value for the CSRF token from my session stored in the database.
Any ideas on what is going on here?
Thanks
If you're not having problems with sessions elsewhere in your code then this may be a form issue and not a database session issue. Make sure that the CSRF input element in your form...:
SomeForm.php
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\Form\Form;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
use Zend\Validator;
class SomeForm extends Form
{
public function __construct($name = null)
{
parent::__construct('csrf-eg');
$this->setAttribute('method', 'post');
// CSRF field
$this->add
(
array
(
'type' => 'Zend\Form\Element\Csrf',
'name' => 'your_csrf',
'attributes' => array
(
'type' => 'text',
'id' => 'divIDforCsrfField',
),
'options' => array
(
'csrf_options' => array
(
'timeout' => 600
)
),
)
);
}
}
actually has a filter attached and validation for the CSRF field:
SomeFormFilter.php
namespace Some\Form;
use Zend\Form\Element;
use Zend\Form\Fieldset;
use Zend\InputFilter\Input;
use Zend\InputFilter\InputFilter;
use Zend\Validator;
use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
class SomeFormFilter implements InputFilterAwareInterface
{
public $your_csrf;
protected $inputFilter;
public function exchangeArray($data){...}
public function setInputFilter(InputFilterInterface $inputFilter){...}
public function getInputFilter()
{
if (!$this->inputFilter){
$inputFilter = new InputFilter();
$factory = new InputFactory();
// CSRF field
$inputFilter->add
(
$factory->createInput
(
array
(
'name' => 'your_csrf',
'required' => true,
'validators' => array
(
array
(
'name' => 'Csrf',
'options' => array
(
'messages' => array
(
Validator\Csrf::NOT_SAME => 'Your CSRF validation msg',
),
),
),
)
)
);
$this->inputFilter = $inputFilter;
}
return $this->inputFilter;
}
}
To get your csrf validation messages into your Action:
$SomeForm = new SomeForm();
$SomeFormValues = $this->getRequest()->getPost();
$SomeForm->setData($SomeFormValues);
$SomeFormFilter = new SomeFormFilter();
$SomeForm->setInputFilter($SomeFormFilter->getInputFilter());
if ($SomeForm->isValid($SomeFormValues))
{
$validatedData = $SomeForm->getData();
}
else
{
$SomeFormMessages = $SomeForm->getMessages();
}
$viewModel = new ViewModel
(
array
(
'SomeFormMessages' => $SomeFormMessages,
)
);
return $viewModel;
I am attempting to access the result set from a model query in the view. I have the following:
Controller:
$courseId = $this->session->userdata('courseId');
//echo "Course: ".$courseId;
if(isset($courseId) && $courseId != '')
{
$result = $this->Course_model->loadBasicDetailsEdit($courseId);
$data['basicCourseDetails'] = $result;
$this->load->view('course/basicDetails', $data);
}
Model:
function loadBasicDetailsEdit($courseId)
{
$this->db->select('*');
$this->db->where('course_id', $courseId);
$this->db->from('course');
$query = $this->db->get();
if ( $query->num_rows() > 0 )
{
return $query->result();
} else {
return FALSE;
}
}
and in the view I tried to print_r() and got this:
Array ( [0] => stdClass Object ( [course_id] => 8 [title] => Photography [summary] => [description] => [price] => [member_id] => 12 [category] => [audience] => [goals] => [date] => 2013-09-26 [production] => 0 ) )
I tried to access this using $basicCourseDetails->title or $basicCourseDetails['title']
but neither are working. Any hint as to why this is happening?
Regards,
try this:
foreach($basicCourseDetails as $basic){
echo($basic->title);
}
or something like this:
echo($basicCourseDetails[0]->title);
This is an array of objects
Array ( [0] => stdClass Object ( [course_id] => 8 [title] => Photography [summary] => [description] => [price] => [member_id] => 12 [category] => [audience] => [goals] => [date] => 2013-09-26 [production] => 0 ) )
Contains one stdObject in the array, so, first objects is 0, if there were more, then second item could have index 1 and so on. To retrieve data from the first (here is only one) stdobject you may use
echo $basicCourseDetails[0]->title; // title will be printed
You can send data to the view page by this line of code which is mentioned in above question.
$result = $this->Course_model->loadBasicDetailsEdit($courseId);
$data['basicCourseDetails'] = $result;
$this->load->view('course/basicDetails', $data);
But when you will access these data in view then you need to access all data one by one by using foreachloop in the view page.
For example if you have a view page like basic_details.php inside course folder then you need to write code like this to access these data.
foreach ($basicCourseDetails as $key => $value) {
$name = $value->name;
}
The above foreachloop can be written in view page where you want to access data.