So I've got an issue with element-api in php, which is a plugin for CRAFT cms, could someone help - what am I doing wrong here? I can't print time.json - always getting :
{"error":{"code":0,"message":"Class name must be a valid object or a
string"}}
here's the code:
<?php
use Craft;
use craft\elements\Entry;
use craft\controllers\TemplatesController;
use League\Fractal\TransformerAbstract;
use craft\web\View;
use craft\elements\User;
return [
'endpoints' => [
'tooltip-modal.json' => function() {
$affiliateId = Craft::$app->request->getParam('entryId');
$siteLanguage = Craft::$app->request->getParam('siteLanguage');
$data = [
'elementType' => Entry::class,
'paginate' => false,
'transformer' => function(Entry $affiliate) {
$view = Craft::$app->getView();
// $view->setTemplateMode($view::TEMPLATE_MODE_SITE);
return [
'affiliate' => $view->renderTemplate('modals/tooltipAffModal.twig', ['affiliate' => $affiliate])
];
},
'criteria' => ['id' => $affiliateId, 'site' => $siteLanguage]
];
return $data;
},
'time.json' => function() {
// $userId = Craft::$app->request->request->getParam('userId');
$data = [
'elementType' => User::class,
'paginate' => false,
'transformer' => function(User $user) {
return [
'date' => $user->winHistory::find()->orderBy(['id' => SORT_DESC])->one()
];
},
// 'criteria' => ['id' => $affiliateId, 'site' => $siteLanguage]
];
return $data;
}
]
];
I am trying to create a class to clean data for a brand before adding it to my database. As you can see I have added general filters (which can be used elsewhere). On the other hand, some fields will need a personalized cleaning. That's why I created 'function' in my array. My code is currently functional however the "create_function" function is deprecated and I would like to remove it but I cannot find an alternative without using "eval". Can you help me find a solution? Thank you.
<?php
class VehMarques
{
private static $fields_allowed = [
'_id' =>
[
'instanceof' => '\MongoDB\BSON\ObjectID',
],
'name' =>
[
'function' => 'if(!isset($name) && !isset($age)){return false;}',
],
'user' =>
[
'required',
'instanceof' => '\MongoDB\BSON\ObjectID',
],
'centre' =>
[
'required',
'instanceof' => '\MongoDB\BSON\ObjectID',
],
'time' =>
[
'instanceof' => 'MongoDB\BSON\UTCDateTime',
],
];
public static function add(array $fields)
{
$fields_options=array();
foreach(self::$fields_allowed as $key => $val)
{
foreach($val as $key1 => $val1)
{
if(in_array($val1, array('required')))
{
$fields_options[$val1][$key] = null;
}
else
{
$fields_options[$key1][$key] = $val1;
}
}
}
if(!empty(self::$fields_allowed) && !empty(array_diff_key($fields, self::$fields_allowed)))
{
return false;
}
if(!empty($fields_options['function']))
{
foreach($fields_options['function'] as $func)
{
$func = preg_replace('/\$([a-zA-Z0-9]+)/', '$fields[\'$1\']', $func);
if(create_function('$fields', $func)($fields) === false)
{
return false;
}
}
}
if(!empty($fields_options['required']) && !empty(array_diff_key($fields_options['required'], $fields)))
{
return false;
}
if(!empty($fields_options['instanceof']))
{
foreach($fields_options['instanceof'] as $key => $val)
{
if(!($fields[$key] instanceof $val))
{
return false;
}
}
}
if(!isset($fields['_id']))
{
$fields['_id'] = new \MongoDB\BSON\ObjectID();
}
if(!isset($fields['time']))
{
$fields['time'] = new MongoDB\BSON\UTCDateTime();
}
return true;
}
}
$insert_marque = array(
'_id' => new \MongoDB\BSON\ObjectID(),
'name' => 'Test',
'user' => new \MongoDB\BSON\ObjectID(),
'centre' => new \MongoDB\BSON\ObjectID(),
'time' => new MongoDB\BSON\UTCDateTime()
);
var_dump(VehMarques::add($insert_marque));
?>
I am using Laravel Tastyigniter system in which I want to show the locations name in dropdown in Menus module according to locations added by admin which is currently logged in.
For Example, If admin A added two locations such as location A and location B and
admin B added two locations such as location C and location D resp.
Note - The locations are getting saved in database with created_by column which is id of admin adding the location.
A) What supposed to happen -
If I logged in as admin A then in location dropdown Location A and Location B should get display
If I logged in as admin B then in location dropdown Location C and Location D should get display.
B) What is happening currently -
For both the admins all the 4 locations are getting displayed.
C) Following is the Code -
Here is Menus_model.php
<?php namespace Admin\Models;
use Admin\Traits\Locationable;
use Igniter\Flame\Database\Traits\Purgeable;
class Menus_model extends Model
{
use Purgeable;
use Locationable;
const LOCATIONABLE_RELATION = 'locations';
public $relation = [
'morphToMany' => [
'locations' => ['Admin\Models\Locations_model', 'name' =>
'locationable'],
],
];
protected $purgeable = ['locations'];
}
Here is menus_model.php which is present under models->config
<?php
$config['form']['tabs'] = [
'fields' => [
'locations' => [
'label' => 'Location',
'type' => 'relation',
'span' => 'right',
'valueFrom' => 'locations',
'nameFrom' => 'location_name',
'locationAware' => 'hide',
],
],
];
return $config;
Here is the Locations_model.php file code under models folder
<?php namespace Admin\Models;
use Admin\Traits\HasDeliveryAreas;
use Admin\Traits\HasWorkingHours;
use Igniter\Flame\Database\Attach\HasMedia;
use Igniter\Flame\Database\Traits\HasPermalink;
use Igniter\Flame\Database\Traits\Purgeable;
use Igniter\Flame\Location\Models\AbstractLocation;
use DB;
/**
* Locations Model Class
*
* #package Admin
*/
class Locations_model extends AbstractLocation
{
use HasWorkingHours;
use HasDeliveryAreas;
use HasPermalink;
use Purgeable;
use HasMedia;
const LOCATION_CONTEXT_SINGLE = 'single';
const LOCATION_CONTEXT_MULTIPLE = 'multiple';
protected $appends = ['location_thumb'];
protected $hidden = ['options'];
public $casts = [
'location_country_id' => 'integer',
'location_lat' => 'double',
'location_lng' => 'double',
'offer_delivery' => 'boolean',
'offer_collection' => 'boolean',
'delivery_time' => 'integer',
'collection_time' => 'integer',
'last_order_time' => 'integer',
'reservation_time_interval' => 'integer',
'reservation_stay_time' => 'integer',
'location_status' => 'boolean',
'options' => 'serialize',
'location_city' => 'integer',
'region_id'=>'integer',
];
public $relation = [
'hasMany' => [
'working_hours' => ['Admin\Models\Working_hours_model', 'delete' =>
TRUE],
'delivery_areas' => ['Admin\Models\Location_areas_model', 'delete'
=> TRUE],
'reviews' => ['Admin\Models\Reviews_model', 'delete' => TRUE],
],
'belongsTo' => [
'country' => ['System\Models\Countries_model', 'otherKey' =>
'country_id', 'foreignKey' => 'location_country_id'],
'city' => ['Admin\Models\City_model', 'otherKey' => 'city_id', 'foreignKey' => 'location_city'],
'region' => ['Admin\Models\Region_model', 'otherKey' => 'region_id', 'foreignKey' => 'region_id'],
],
'belongsToMany' => [
'tables' => ['Admin\Models\Tables_model', 'table' => 'location_tables'],
'cuisines' => ['Admin\Models\Cuisines_model', 'table' => 'location_cuisines'],
],
];
protected $purgeable = ['tables', 'delivery_areas','cuisines'];
public $permalinkable = [
'permalink_slug' => [
'source' => 'location_name',
'controller' => 'local',
],
];
public $mediable = [
'thumb',
'gallery' => ['multiple' => TRUE],
];
protected static $allowedSortingColumns = [
'distance asc', 'distance desc',
'reviews_count asc', 'reviews_count desc',
'location_id asc', 'location_id desc',
'location_name asc', 'location_name desc',
];
public $url;
protected static $defaultLocation;
public static function onboardingIsComplete()
{
if (!$defaultId = params('default_location_id'))
return FALSE;
if (!$model = self::isEnabled()->find($defaultId))
return FALSE;
return isset($model->getAddress()['location_lat'])
AND isset($model->getAddress()['location_lng'])
AND ($model->hasDelivery() OR $model->hasCollection())
AND isset($model->options['hours'])
AND $model->delivery_areas->where('is_default', 1)->count() > 0;
}
public function getWeekDaysOptions()
{
return ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
}
//
// Events
//
protected function afterFetch()
{
$this->parseOptionsValue();
}
protected function beforeSave()
{
$this->parseOptionsValue();
}
protected function afterSave()
{
$this->performAfterSave();
}
protected function beforeDelete()
{
Location_tables_model::where('location_id', $this->getKey())->delete();
Location_cuisines_model::where('location_id', $this->getKey())->delete();
}
//
// Scopes
//
/**
* Scope a query to only include enabled location
*
* #return $this
*/
public function scopeIsEnabled($query)
{
return $query->where('location_status', 1);
}
public function scopeListFrontEnd($query, array $options = [])
{
extract(array_merge([
'page' => 1,
'pageLimit' => 20,
'sort' => null,
'search' => null,
'latitude' => null,
'longitude' => null,
], $options));
if ($latitude AND $longitude)
$query->selectDistance($latitude, $longitude);
$searchableFields = ['location_name', 'location_address_1', 'location_address_2', 'location_city',
'location_state', 'location_postcode', 'description'];
if (!is_array($sort)) {
$sort = [$sort];
}
foreach ($sort as $_sort) {
if (in_array($_sort, self::$allowedSortingColumns)) {
$parts = explode(' ', $_sort);
if (count($parts) < 2) {
array_push($parts, 'desc');
}
[$sortField, $sortDirection] = $parts;
$query->orderBy($sortField, $sortDirection);
}
}
$search = trim($search);
if (strlen($search)) {
$query->search($search, $searchableFields);
}
return $query->paginate($pageLimit, $page);
}
//
// Accessors & Mutators
//
public function getLocationThumbAttribute()
{
return $this->hasMedia() ? $this->getThumb() : null;
}
public function getDeliveryTimeAttribute($value)
{
return (int)$value;
}
public function getCollectionTimeAttribute($value)
{
return (int)$value;
}
public function getFutureOrdersAttribute($value)
{
return (bool)$value;
}
public function getReservationTimeIntervalAttribute($value)
{
return (int)$value;
}
//
// Helpers
//
public function setUrl($suffix = null)
{
if (is_single_location())
$suffix = '/menus';
$this->url = site_url($this->permalink_slug.$suffix);
}
public function hasGallery()
{
return $this->hasMedia('gallery');
}
public function getGallery()
{
$gallery = array_get($this->options, 'gallery');
$gallery['images'] = $this->getMedia('gallery');
return $gallery;
}
public function parseOptionsValue()
{
$value = #unserialize($this->attributes['options']) ?: [];
$this->parseHoursFromOptions($value);
$this->parseAreasFromOptions($value);
$this->attributes['options'] = #serialize($value);
return $value;
}
public function listAvailablePayments()
{
$result = [];
$payments = array_get($this->options, 'payments', []);
$paymentGateways = Payments_model::listPayments();
foreach ($paymentGateways as $payment) {
if ($payments AND !in_array($payment->code, $payments)) continue;
$result[$payment->code] = $payment;
}
return collect($result);
}
public function performAfterSave()
{
$this->restorePurgedValues();
if (array_key_exists('hours', $this->options)) {
$this->addOpeningHours($this->options['hours']);
}
if (array_key_exists('delivery_areas', $this->attributes)) {
$this->addLocationAreas($this->attributes['delivery_areas']);
}
if (array_key_exists('tables', $this->attributes)) {
$this->addLocationTables($this->attributes['tables']);
}
if (array_key_exists('cuisines', $this->attributes)) {
$this->addLocationCuisines($this->attributes['cuisines']);
}
}
public static function getDefault()
{
if (self::$defaultLocation !== null) {
return self::$defaultLocation;
}
$defaultLocation = self::isEnabled()->where('location_id', params('default_location_id'))->first();
if (!$defaultLocation) {
$defaultLocation = self::isEnabled()->first();
if ($defaultLocation) {
params('default_location_id', $defaultLocation->getKey());
params()->save();
}
}
return self::$defaultLocation = $defaultLocation;
}
/**
* Create a new or update existing location tables
*
* #param array $tables
*
* #return bool
*/
public function addLocationTables($tables = [])
{
return $this->tables()->sync($tables);
}
public function addLocationCuisines($cuisines = [])
{
return $this->cuisines()->sync($cuisines);
}
}
Here is locations_model.php which is present under models->config folder
<?php
$config['form']['tabs'] = [
'defaultTab' => 'lang:admin::lang.locations.text_tab_general',
'fields' => [
'location_name' => [
'label' => 'lang:admin::lang.label_name',
'type' => 'text',
'span' => 'left',
],
'location_email' => [
'label' => 'lang:admin::lang.label_email',
'type' => 'text',
'span' => 'right',
],
'location_telephone' => [
'label' => 'lang:admin::lang.locations.label_telephone',
'type' => 'text',
'span' => 'left',
],
'location_status' => [
'label' => 'lang:admin::lang.label_status',
'type' => 'switch',
'default' => 1,
'span' => 'right',
],
'created_by' => [
'type' => 'hidden',
'default' => isset($_SESSION['user_id']) ? $_SESSION['user_id'] : '',
],
],
];
return $config;
UPDATED
Basically I want to diaply locations in menus form , Currently in menus form all the locations are getting display and the code for this is mentioned below
This is Menus.php controller
<?php namespace Admin\Controllers;
use Admin\Classes\AdminController;
use Admin\Models\Menu_options_model;
use AdminMenu;
use ApplicationException;
class Menus extends AdminController
{
public $implement = [
'Admin\Actions\ListController',
'Admin\Actions\FormController',
'Admin\Actions\LocationAwareController',
];
public $listConfig = [
'list' => [
'model' => 'Admin\Models\Menus_model',
'title' => 'lang:admin::lang.menus.text_title',
'emptyMessage' => 'lang:admin::lang.menus.text_empty',
'defaultSort' => ['menu_id', 'DESC'],
'configFile' => 'menus_model',
],
];
protected $requiredPermissions = 'Admin.Menus';
public function __construct()
{
parent::__construct();
AdminMenu::setContext('menus');
}
public function edit_onChooseMenuOption($context, $recordId)
{
$menuOptionId = post('Menu._options');
if (!$menuOption = Menu_options_model::find($menuOptionId))
throw new ApplicationException('Please select a menu option to
attach');
$model = $this->asExtension('FormController')->formFindModelObject($recordId);
$menuItemOption = $model->menu_options()->create(['option_id' => $menuOptionId]);
$menuOption->option_values()->get()->each(function ($model) use ($menuItemOption) {
$menuItemOption->menu_option_values()->create([
'menu_option_id' => $menuItemOption->menu_option_id,
'option_value_id' => $model->option_value_id,
'new_price' => $model->price,
]);
});
$model->reload();
$this->asExtension('FormController')->initForm($model, $context);
flash()->success(sprintf(lang('admin::lang.alert_success'), 'Menu item option attached'))->now();
$formField = $this->widgets['form']->getField('menu_options');
return [
'#notification' => $this->makePartial('flash'),
'#'.$formField->getId('group') => $this->widgets['form']->renderField($formField, [
'useContainer' => FALSE,
]),
];
}
}
Below is Locaations.php controller
<?php namespace Admin\Controllers;
use Admin\Facades\AdminLocation;
use Admin\Models\Locations_model;
use AdminMenu;
use Exception;
use Geocoder;
class Locations extends \Admin\Classes\AdminController
{
public $implement = [
'Admin\Actions\ListController',
'Admin\Actions\FormController',
];
public $listConfig = [
'list' => [
'model' => 'Admin\Models\Locations_model',
'title' => 'lang:admin::lang.locations.text_title',
'emptyMessage' => 'lang:admin::lang.locations.text_empty',
'defaultSort' => ['location_id', 'DESC'],
'configFile' => 'locations_model',
],
];
protected $requiredPermissions = 'Admin.Locations';
public function __construct()
{
parent::__construct();
AdminMenu::setContext('locations', 'restaurant');
}
public function remap($action, $params)
{
if ($action != 'settings' AND AdminLocation::check())
return $this->redirect('locations/settings');
return parent::remap($action, $params);
}
public function settings($context = null)
{
if (!AdminLocation::check())
return $this->redirect('locations');
$this->asExtension('FormController')->edit('edit', $this-
>getLocationId());
}
public function index_onSetDefault($context = null)
{
$defaultId = post('default');
if (Locations_model::updateDefault(['location_id' => $defaultId])) {
flash()->success(sprintf(lang('admin::lang.alert_success'),
lang('admin::lang.locations.alert_set_default')));
}
return $this->refreshList('list');
}
public function settings_onSave($context = null)
{
try {
$this->asExtension('FormController')->edit_onSave('edit',
params('default_location_id'));
return $this->refresh();
}
catch (Exception $ex) {
$this->handleError($ex);
}
}
public function listOverrideColumnValue($record, $column, $alias = null)
{
if ($column->type != 'button')
return null;
if ($column->columnName != 'default')
return null;
$attributes = $column->attributes;
$column->iconCssClass = 'fa fa-star-o';
if ($record->getKey() == params('default_location_id')) {
$column->iconCssClass = 'fa fa-star';
}
return $attributes;
}
public function formExtendQuery($query)
{
if ($locationId = $this->getLocationId())
$query->where('location_id', $locationId);
}
public function formAfterSave($model)
{
if (post('Location.options.auto_lat_lng')) {
if ($logs = Geocoder::getLogs())
flash()->error(implode(PHP_EOL, $logs))->important();
}
}
}
Views
Now the n views folder there is folder names menus and under that folder there is create.php file for displaying create menu form
The code in views->menus->create.php file is below
<div class="row-fluid">
<?= form_open(current_url(),
[
'id' => 'edit-form',
'role' => 'form',
'method' => 'POST',
]
); ?>
<?= $this->renderForm(); ?>
<?= form_close(); ?>
</div>
FormController
Now the renderForm() function is present at path app/admin/actions/FormController.php which we have defined in Locations and Menus controller under public $implement = ['Admin\Actions\FormController'];
Ther renderForm() function is as follow
public function renderForm($options = [])
{
if (!$this->formWidget) {
throw new Exception(lang('admin::lang.form.not_ready'));
}
if (!is_null($this->toolbarWidget)) {
$form[] = $this->toolbarWidget->render();
}
$form[] = $this->formWidget->render($options);
return implode(PHP_EOL, $form);
}
Widgets
At last the there are widgets for input fields like select, text, radio, checkbox etc. In our case we have widget name field_selectlist, which is present at path app/admin/widgets/form/field_selectlist.php
The field_selectlist.php file has code as below
<?php
$fieldOptions = $field->options();
//print_r($fieldOptions);die; All the locations are displaying here.
$isCheckboxMode = $field->config['mode'] ?? 'checkbox';
$selectMultiple = $isCheckboxMode == 'checkbox';
$checkedValues = (array)$field->value;
$enableFilter = (count($fieldOptions) > 20);
?>
<div class="control-selectlist">
<select
data-control="selectlist"
id="<?= $field->getId() ?>"
name="<?= $field->getName() ?><?= $selectMultiple ? '[]' : '' ?>"
<?php if ($field->placeholder) { ?>data-non-selected-text="<?=
e(lang($field->placeholder)) ?>"<?php } ?>
<?= $selectMultiple ? 'multiple="multiple"' : '' ?>
data-enable-filtering="<?= $enableFilter; ?>"
data-enable-case-insensitive-filtering="<?= $enableFilter; ?>"
<?= $field->getAttributes() ?>>
<?php if ($field->placeholder) { ?>
<option value=""><?= e(lang($field->placeholder)) ?></option>
<?php } ?>
<?php
foreach ($fieldOptions as $value => $option) { ?>
<?php
if (!is_array($option)) $option = [$option];
if ($field->disabled AND !in_array($value, $checkedValues)) continue;
?>
<option
<?= in_array($value, $checkedValues) ? 'selected="selected"' : '' ?>
value="<?= $value ?>">
<?= e(is_lang_key($option[0]) ? lang($option[0]) : $option[0]) ?>
<?php if (isset($option[1])) { ?>
<span><?= e(is_lang_key($option[1]) ? lang($option[1]) :
$option[1]) ?></span>
<?php } ?>
</option>
<?php } ?>
I am trying to implement the 2amigos SelectizeDropDownList widget in a form to add new values to a table directly within the dropdown.
I am using the model Book and the Model Author so basically want to be able to add a new author in the book form.
This is the book controller at the update function:
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}
This is the form:
<?=
$form->field($model, 'author_id')->widget(SelectizeDropDownList::className(), [
// calls an action that returns a JSON object with matched
// tags
'loadUrl' => ['author/list'],
'value' => $authors,
'items' => \yii\helpers\ArrayHelper::map(\common\models\author::find()->orderBy('name')->asArray()->all(), 'id', 'name'),
'options' => [
'class' => 'form-control',
'id' => 'id'
],
'clientOptions' => [
'valueField' => 'id',
'labelField' => 'name',
'searchField' => ['name'],
'autosearch' => ['on'],
'create' => true,
'maxItems' => 1,
],
])
?>
And this is the function author controller:
public function actionList($query) {
$models = Author::findAllByName($query);
$items = [];
foreach ($models as $model) {
$items[] = ['id' => $model->id, 'name' => $model->name];
}
Yii::$app->response->format = \Yii::$app->response->format = 'json';
return $items;
}
The form works fine to load, filter, search and add new items.
But it is not inserting the new typed attribute in the author table.
Do I need to add something in the book controller?
How can I check if it is a new value or a change of an existing author?
Thanks a lot
I made it work with the following code, not sure the most elegant because i am checking the if the author_id is a number or a string.
In my case the author won't be a number anyway.
public function actionUpdate($id) {
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post())) {
$x = Yii::$app->request->post('Book');
$new_author = $x['author_id'];
if (!is_numeric($new_author)) {
$author = new Author();
$author->name = $new_author;
$author->save();
$model->author_id = $author->id;
}
if ($model->save()) {
return $this->redirect(['index']);
}
} else {
return $this->render('update', [
'model' => $model,
'categories' => BookCategory::find()->active()->all(),
'publishers' => Publisher::find()->all(),
'copirights' => Copiright::find()->all(),
'authors' => Author::find()->all(),
]);
}
}
How do I apply a filter to a field element with the contents of an array?
For example:
$this->add(
"name" => "tags",
"type" => "text",
"filter" => array(
array("name" => "StripTags"),
array("name" => "StringTrim")
)
);
$tags[0] = "PHP";
$tags[1] = "CSS";
If I attempt to filter I receive an error saying a scalar object is excepted, array given.
This isn't really possible at this time. Your best bet is to use a Callback filter and filter each Item individually. Something like this
$this->add(
"name" => "tags",
"type" => "text",
"filter" => array(
array("name" => "Callback", "options" => array(
"callback" => function($tags) {
$strip = new \Zend\Filter\StripTags();
$trim = new \Zend\Filter\StringTrim();
foreach($tags as $key => $tag) {
$tag = $strip->filter($tag);
$tag = $trim->filter($tag);
$tags[$key] = $tag;
}
return $tags;
}))
)
);
I realize this is old but you can specify the input type as ArrayInput and InputFilter will handle it as expected:
"name" => "tags",
"type" => "Zend\\InputFilter\\ArrayInput", // Treat this field as an array of inputs
"filter" => array(
array("name" => "StripTags"),
array("name" => "StringTrim")
)
I've made a CollectionValidator that applies an existing validator to all items in an array.
I'm using it with Apigility as such:
'input_filter_specs' => [
'Api\\Contact\\Validator' => [
[
'name' => 'addresses',
'required' => false,
'filters' => [],
'validators' => [
[
'name' => 'Application\\Validator\\CollectionValidator',
'options' => ['validator' => 'Api\\Address\\Validator']
]
],
'description'=> 'List of addresses for contact'
],
[
'name' => 'birthdate',
# ...
]
],
]
I'm not sure if this is how you would use a validator inside a controller, but probably something like this:
new Collection(array('validator' => 'Zend\Validator\CreditCard'))
It returns validation_messages per index. Let's say it was REST POST request to create a contact, it indicates that the second address contains an error in the zipcode field.
{
"detail": "Failed Validation",
"status": 422,
"title": "Unprocessable Entity",
"type": "http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html",
"validation_messages": {
"addresses": {
"1": {
"zipcode": {
"notAlnum": "The input contains characters which are non alphabetic and no digits"
}
}
},
"birthdate": {
"dateInvalidDate": "The input does not appear to be a valid date"
}
}
}
The Collection validator:
<?php
namespace Application\Validator;
class Collection extends \Zend\Validator\AbstractValidator implements \Zend\ServiceManager\ServiceLocatorAwareInterface {
protected $serviceLocator;
protected $em;
protected $messages;
protected $options = array(
'validator' => null
);
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $serviceLocator) {
$this->serviceLocator = $serviceLocator->getServiceLocator();
}
public function getServiceLocator() {
return $this->serviceLocator;
}
public function isValid($array) {
$inputFilterManager = $this->getServiceLocator()->get('inputfiltermanager');
$validatorName = $this->getOption('validator');
$this->messages = [];
$isvalid = true;
foreach($array as $index => $item) {
$inputFilter = $inputFilterManager->get($validatorName);
$inputFilter->setData($item);
$isvalid = $isvalid && $inputFilter->isValid($item);
foreach($inputFilter->getMessages() as $field => $errors) {
foreach($errors as $key => $string) {
$this->messages[$index][$field][$key] = $string;
}
}
}
return $isvalid;
}
public function getMessages() {
return $this->messages;
}
}
Current limitations:
No support for translation
Only the errors for the first erroneous array item are returned.
I had a very simular issue and I was able to solve it with Zend\Form\Element\Collection.
With the Collection Element I was able to validate inputs that looks like
$post = [
[
'idUser' => 1,
'address' => 'foo street',
],
[
'idUser' => 2,
'address' => 'bar street',
],
];
For a more detailed explanation check out the Zend Documentation and this working example