Failed to Insert Data with zend-form - php

I have a mode named Album as follows:
namespace Album\Model;
// Add the following import statements:
use DomainException;
use Zend\Filter\StringTrim;
use Zend\Filter\StripTags;
use Zend\Filter\ToInt;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;
use Zend\Validator\StringLength;
class Album {
public $id;
public $artist;
public $title;
private $inputFilter;
public function exchangeArray(array $data) {
$this->id = !empty($data['_id']) ? $data['_id'] : null;
$this->artist = !empty($data['artist']) ? $data['artist'] : null;
$this->title = !empty($data['title']) ? $data['title'] : null;
}
public function setInputFilter(InputFilterInterface $inputFilter) {
throw new DomainException(sprintf(
'%s does not allow injection of an alternate input filter', __CLASS__
));
}
public function getInputFilter() {
if ($this->inputFilter) {
return $this->inputFilter;
}
$inputFilter = new InputFilter();
$inputFilter->add([
'name' => 'id',
'required' => true,
'filters' => [
['name' => StripTags::class],
['name' => StringTrim::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100,
],
],
],
]);
$inputFilter->add([
'name' => 'title',
'required' => true,
'filters' => [
['name' => StripTags::class],
['name' => StringTrim::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100,
],
],
],
]);
$inputFilter->add([
'name' => 'artist',
'required' => true,
'filters' => [
['name' => StripTags::class],
['name' => StringTrim::class],
],
'validators' => [
[
'name' => StringLength::class,
'options' => [
'encoding' => 'UTF-8',
'min' => 1,
'max' => 100,
],
],
],
]);
$this->inputFilter = $inputFilter;
return $this->inputFilter;
}
public function getArrayCopy() {
return [
'id' => $this->id,
'artist' => $this->artist,
'title' => $this->title,
];
}
}
And a form named AlbumForm as follows:
namespace Album\Form;
use Zend\Form\Form;
class AlbumForm extends Form
{
public function __construct($name = null)
{
// We will ignore the name provided to the constructor
parent::__construct('album');
$this->add([
'name' => 'id',
'type' => 'hidden',
]);
$this->add([
'name' => 'title',
'type' => 'text',
'options' => [
'label' => 'Title',
],
]);
$this->add([
'name' => 'artist',
'type' => 'text',
'options' => [
'label' => 'Artist',
],
]);
$this->add([
'name' => 'submit',
'type' => 'submit',
'attributes' => [
'value' => 'Go',
'id' => 'submitbutton',
],
]);
}
}
And a controller named AlbumController in which I have addAction as follows:
public function addAction() {
$form = new AlbumForm();
$form->get('submit')->setValue('Add');
$request = $this->getRequest();
if (!$request->isPost()) {
return ['form' => $form];
}
$album = new Album();
$form->setInputFilter($album->getInputFilter());
$form->setData($request->getPost());
if (!$form->isValid()) {
return ['form' => $form];
}
$album->exchangeArray($form->getData());
$this->table->saveAlbum($album);
return $this->redirect()->toRoute('album');
}
The module.config.php file is as follows and I see no problem with routing:
return [
// The following section is new and should be added to your file:
'router' => [
'routes' => [
'album' => [
'type' => Segment::class,
'options' => [
'route' => '/album[/:action[/:id]]',
'constraints' => [
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
],
'defaults' => [
'controller' => Controller\AlbumController::class,
'action' => 'index',
],
],
],
],
],
'view_manager' => [
'template_path_stack' => [
'album' => __DIR__ . '/../view',
],
],
];
but, I cannot add a new album to the db. Once, I press fill the form and hit 'Add' in the Add form, nothing is added and I remain in the same page. Does anyone know where I have messed up?

You have an error on the Model Album. It could have the following change:
$inputFilter->add([
'name' => 'id',
'required' => true,
'filters' => [
['name' => ToInt::class],
],
]);

Related

How to create recursive hierarchy in webonyx/graphql?

I want to create hierarchy tree-like structure.
$fieldOptionsType = new ObjectType([
'name' => 'Options',
'fields' => [
'nested' => [
'type' => Type::listOf($fieldType), // $fieldType not initialized yet.
],
],
]);
$fieldType = new ObjectType([
'name' => 'Field',
'fields' => [
'options' => [
'type' => $fieldOptionsType,
],
],
]);
I have problem, that variable $fieldType used first isnt created yet. How to solve this?
Try with the below code, this will build a tree structure.
private ObjectType $fieldOptionsType;
private ObjectType $fieldType;
private array $objectTypes;
$this->fieldOptionsType = $this->objectTypes['Options'] ??= new ObjectType([
'name' => 'Options',
'fields' => [
'nested' => [
'type' => fn() => Type::listOf($this->fieldType),
],
],
]);
$this->fieldType = $this->objectTypes['Field'] ??= new ObjectType([
'name' => 'Field',
'fields' => [
'options' => [
'type' => fn() => $this->fieldOptionsType,
],
],
]);

Populate Multi select drop drop down zend 3

Hello everyone i am getting really hard time populating my multi select drop down in my form.
what i have tried so far was adding factory for my form which is like this
class MovieFormFactory
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$entityManager = $container->get('doctrine.entitymanager.orm_default');
$actors = $entityManager->getRepository(Actor::class)->findAll();
$form = new MovieForm();
$form->setActors($data);
return $form;
}
}
my form
Class MovieForm extends Form
{
private $actors = [];
public function setActors($actorsData){
$this->actors = $actors
}
public function __construct()
{
parent::__construct('post-form');
$this->setAttribute('method', 'post');
$this->addElements();
$this->addInputFilter();
$this->add([
'type' => 'select',
'name' => 'actors',
'attributes' => [
'id' => 'actors',
'multiple' => true
],
'options' => [
'label' => 'Actors',
'value_options' => $this->actors,
],
]);
$this->add([
'type' => 'select',
'name' => 'directors',
'attributes' => [
'id' => 'directors',
],
'options' => [
'label' => 'Director',
'value_options' => $this->getOptionForSelect($directors),
],
]);
}
/**
* This method adds elements to form (input fields and submit button).
*/
protected function addElements()
{
// Add "title" field
$this->add([
'type' => 'text',
'name' => 'title',
'attributes' => [
'id' => 'title'
],
'options' => [
'label' => 'Title',
],
]);
// Add "description" field
$this->add([
'type' => 'textarea',
'name' => 'description',
'attributes' => [
'id' => 'description'
],
'options' => [
'label' => 'Description',
],
]);
// Add "tags" field
// $this->add([
// 'type' => 'text',
// 'name' => 'actors',
// 'attributes' => [
// 'id' => 'actors',
// 'multiple' => 'multiple'
// ],
// 'options' => [
// 'label' => 'Actors',
// 'value_options' => $this->getOptionForSelect(),
// ],
// ]);
// Add "status" field
$this->add([
'type' => 'select',
'name' => 'status',
'attributes' => [
'id' => 'status'
],
'options' => [
'label' => 'Status',
'value_options' => [
MovieStatusEnum::STATUS_DRAFT => MovieStatusEnum::STATUS_DRAFT,
MovieStatusEnum::STATUS_PUBLISHED => MovieStatusEnum::STATUS_PUBLISHED,
]
],
]);
// Add the submit button
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Create',
'id' => 'submitbutton',
],
]);
}
/**
* This method creates input filter (used for form filtering/validation).
*/
private function addInputFilter()
{
$inputFilter = new InputFilter();
$this->setInputFilter($inputFilter);
$inputFilter->add([
'name' => 'title',
'required' => true,
'filters' => [
['name' => 'StringTrim'],
['name' => 'StripTags'],
['name' => 'StripNewlines'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'min' => 1,
'max' => 1024
],
],
],
]);
$inputFilter->add([
'name' => 'description',
'required' => true,
'filters' => [
['name' => 'StripTags'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'min' => 1,
'max' => 4096
],
],
],
]);
$inputFilter->add([
'name' => 'actors',
'required' => true,
]);
}
private function getOptionForSelect($data)
{
foreach ($data as $person) {
$selectData[$person->getId()] = $person->getName();
}
return $selectData;
}
}
and this is my registered factory in module.config.php
'form_elements' => [
'factories' => [
Form\MovieForm::class => Form\Factory\MovieFormFactory::class
]
],
but nothing seems to work i am unable to show my actors while creating a movie and unable to show selected actors while editing a movie can some please guide me here i am new to zend.
In the constructor, the line value_options' => $this->actors is wrong because $this->actors is not set yet. In your factory you write :
$form = new MovieForm();
$form->setActors($data);
You must therefore declare the public method setActors() in the class MovieForm which will take care of setting up the options array.
public function setActors($array)
{
$this->get('actors')->setValueOptions($array);
}

Additional attributes in select options

In createForm.php I have code:
$this->add([
'type' => Element\Select::class,
'name' => 'subcategory_id',
'options' =>[
'label' => 'Subcategory',
'empty_option' => 'Select...',
'value_options' => $subcategoriesTable->fetchAllSubcategories(),
],
'attributes' => [
'required' => false,
'class' => 'custom-select',
],
]);
In SubcategoriesTable.php
public function fetchAllSubcategories()
{
$sqlQuery = $this->sql->select()->order('sub_name ASC');
$sqlStmt = $this->sql->prepareStatementForSqlObject($sqlQuery);
$handler = $sqlStmt->execute();
$row = [];
foreach($handler as $tuple){
$row[$tuple['subcategory_id']] = $tuple['sub_name'];
}
return $row;
}
And generated records from my database to my form:
<option value="1">Name1</option>
How I can change my code for making additional attribute like this?
<option value="1" data-chained="parent_name">Name1</option>
parent name value is from another select. Also generated in the same way.
You'll have to make a few changes.
Inside SubcategoriesTable, you'll have to retrieve the parent's name (I used parent_name as key, since we don't know what the exact column's name..):
public function fetchAllSubcategories()
{
$sqlQuery = $this->sql->select()->order('sub_name ASC');
$sqlStmt = $this->sql->prepareStatementForSqlObject($sqlQuery);
$handler = $sqlStmt->execute();
$row = [];
foreach($handler as $tuple){
$row[$tuple['subcategory_id']] = [
'sub_name' => $tuple['sub_name'],
'parent_name' => $tuple['parent_name']
];
}
return $row;
}
Then, in CreateForm:
$valueOptions = [];
foreach($subcategoriesTable->fetchAllSubcategories() as $subcategoryId => $subcategory){
$valueOptions[] = [
'value' => $subcategoryId,
'label' => $subcategory['sub_name'],
'attributes' => [
'data-chained' => $subcategory['parent_name']
]
];
}
$this->add([
'type' => Element\Select::class,
'name' => 'subcategory_id',
'options' =>[
'label' => 'Subcategory',
'empty_option' => 'Select...',
'value_options' => $valueOptions,
],
'attributes' => [
'required' => false,
'class' => 'custom-select',
],
]);
Re-reading your question, I saw that you need this value for another select. I suggest you to add parent's ID instead of parent's name, it would be easier to handle thing.
As exemple:
public function formAction() {
$subcagetoryForm = new Form();
$subcagetoryForm->add([
'type' => Select::class,
'name' => 'subcategory_id',
'options' => [
'label' => 'Subcategory',
'empty_option' => 'Select...',
'value_options' => [
[
'value' => 1,
'label' => 'Name 1',
'attributes' => [
'data-chained' => 'parent 1'
]
],
[
'value' => 2,
'label' => 'Name 2',
'attributes' => [
'data-chained' => 'parent 2'
]
]
],
],
'attributes' => [
'required' => false,
'class' => 'custom-select',
],
]);
return [
'subcagetoryForm' => $subcagetoryForm
];
}
In the view:
<?= $this->formElement($this->subcagetoryForm->get('subcategory_id')); ?>
Result:
[]
However, I'll suggest you to create a custom element to remove all this logic from the form. The custom element will be helpful if you'll need it inside another form. You can take a look at this question

yii2: working with yii2-sitemap-module

i used https://github.com/himiklab/yii2-sitemap-module in my yii2 project
this is my console :
return [
'id' => 'basic-console',
'language' => 'fa-IR',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log', 'gii'],
'controllerNamespace' => 'app\commands',
'modules' => [
'gii' => 'yii\gii\Module',
'user' => [
'class' => 'dektrium\user\Module',
'sourceLanguage' => 'en-US',
'languages' => 'fa-IR'
],
'sitemap' => [
'class' => 'himiklab\sitemap\Sitemap',
'models' => [
// your models
'app\modules\news\models\News',
// or configuration for creating a behavior
[
'class' => 'app\modules\news\models\News',
'behaviors' => [
'sitemap' => [
'class' => SitemapBehavior::className(),
'scope' => function ($model) {
/** #var \yii\db\ActiveQuery $model */
$model->select(['url', 'lastmod']);
$model->andWhere(['is_deleted' => 0]);
},
'dataClosure' => function ($model) {
/** #var self $model */
return [
'loc' => Url::to($model->url, true),
'lastmod' => strtotime($model->lastmod),
'changefreq' => SitemapBehavior::CHANGEFREQ_DAILY,
'priority' => 0.8
];
}
],
],
],
],
'urls' => [
// your additional urls
[
'loc' => '/news/all',
'changefreq' => \himiklab\sitemap\behaviors\SitemapBehavior::CHANGEFREQ_DAILY,
'priority' => 0.8,
'news' => [
'publication' => [
'name' => 'Example Blog',
'language' => 'fa',
],
'access' => 'Subscription',
'genres' => 'Blog, UserGenerated',
'publication_date' => 'YYYY-MM-DDThh:mm:ssTZD',
'title' => 'Example Title',
'keywords' => 'example, keywords, comma-separated',
'stock_tickers' => 'NASDAQ:A, NASDAQ:B',
],
'images' => [
[
'loc' => 'http://example.com/image.jpg',
'caption' => 'This is an example of a caption of an image',
'geo_location' => 'City, State',
'title' => 'Example image',
'license' => 'http://example.com/license',
],
],
],
],
'enableGzip' => true, // default is false
'cacheExpire' => 1, // 1 second. Default is 24 hours
],
],
'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
],
'log' => [
'targets' => [
[
'class' => 'yii\log\FileTarget',
'levels' => ['error', 'warning'],
],
],
],
'db' => $db,
],
'params' => $params,
];
this is my web.php:
'urlManager' => [
'enablePrettyUrl' => TRUE,
'showScriptName' => TRUE,
'enableStrictParsing' => FALSE,
'rules' => [
['pattern' => 'sitemap', 'route' => 'sitemap/default/index', 'suffix' => '.xml'],
// ...
],
],
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
'cookieValidationKey' => 'salt',
],
'cache' => [
'class' => 'yii\caching\FileCache',
],
this is my news controller :
use himiklab\sitemap\behaviors\SitemapBehavior;
public function behaviors() {
return [
'sitemap' => [
'class' => SitemapBehavior::className(),
'scope' => function ($model) {
/** #var \yii\db\ActiveQuery $model */
$model->select(['id']);
// $model->andWhere(['is_deleted' => 0]);
},
'dataClosure' => function ($model) {
/** #var self $model */
return [
'loc' => Url::to($model->url, true),
'lastmod' => strtotime($model->lastmod),
'changefreq' => SitemapBehavior::CHANGEFREQ_DAILY,
'priority' => 0.8
];
}
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['get'],
],
],
];
}
Where is my xml file(url)??
What change should I do in my code?
If your controller (sitemap/default/index) is work well.
Your sitemap must be created in root directory via sitemap.xml file name, and accessible from http://your-domain/sitemap.xml URL.
For change it refer to this your code:
'rules' => [
['pattern' => 'sitemap', 'route' => 'sitemap/default/index', 'suffix' => '.xml'],
],
A shorter version of the routing that you can use:
'rules' => [
'sitemap.xml' => 'sitemap/default/index',
And relative to this route url in web: http://website/sitemap.xml
However, you can generate a sitemap without any extensions, which simplifies your work and does not need to understand other people's code. To do this, simply create the controller as in my working example:
<?php
namespace frontend\controllers;
use frontend\models\blog\articles\BlogArticles;
use frontend\models\blog\categories\BlogCategories;
use frontend\models\blog\series\BlogSeries;
use frontend\models\blog\tags\BlogTags;
use yii\web\Controller;
use yii\db\Query;
use Yii;
class SitemapController extends Controller
{
public function actionIndex()
{
//if You want delete cache
// Yii::$app->cache->delete('sitemap');
if (!$xml_sitemap = Yii::$app->cache->get('sitemap')) { // if has cache sitemap
$urls = array();
// all my categories
$articles = BlogArticles::find()->active()->orderCreatedAt()->all();
foreach ($articles as $article) {
$urls[] = array(
'loc' => $article->url,
'lastmod' => date( DATE_W3C, strtotime($article->lastMod) ),
'changefreq' => 'daily',
'priority' => 1.0
);
}
$categories = BlogCategories::find()->orderId()->all();
foreach ($categories as $category) {
$urls[] = array(
'loc' => $category->url,
'changefreq' => 'weekly',
'priority' => 0.8
);
}
$series = BlogSeries::find()->orderId()->all();
foreach ($series as $sery) {
$urls[] = array(
'loc' => $sery->url,
'changefreq' => 'weekly',
'priority' => 0.5
);
}
$tags = BlogTags::find()->orderId()->all();
foreach ($tags as $tag) {
$urls[] = array(
'loc' => $tag->url,
'changefreq' => 'weekly',
'priority' => 0.4
);
}
$xml_sitemap = $this->renderPartial('index', array(
'host' => Yii::$app->request->hostInfo, // your current domain
'urls' => $urls, // с generate urls for sitemap
));
Yii::$app->cache->set('sitemap', $xml_sitemap, 60*60*12); //cache 12 h
}
Yii::$app->response->format = \yii\web\Response::FORMAT_XML;
echo $xml_sitemap;
}
}
And You can see result in live site: https://coderius.biz.ua/sitemap.xml

ZF2 form doesn't validate because of input filter

In ZF2, I have a controller whose action works with the form like this
if ($request->isPost()) {
$this->organizationForm->setInputFilter(new OrganizationFilter());
$this->organizationForm->setData($request->getPost());
if ($this->organizationForm->isValid()) {
// further logic to process
The InputFilter OrganizationFilter is this
class OrganizationFilter extends InputFilter
{
public function __construct()
{
$this->add([
'name' => 'id',
'filters' => [
['name' => 'Int'],
]
]);
$this->add([
'name' => 'name',
'required' => true,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 3,
'max' => 160
]
]
]
]);
}
}
If I comment the line $this->organizationForm->setInputFilter(new OrganizationFilter()), the form gets validated, but with this line, it doesn't work.
How to get it validated?
I couldn't figure out why my code didn't work, but I solved it in another way. For the form with inputs to be validated, I implemented InputFilterProviderInterface. Then in the form, getInputFilterSpecification()
public function getInputFilterSpecification()
{
return [
'name' => [
'required' => true,
'filters' => [
['name' => 'StripTags'],
['name' => 'StringTrim'],
],
'validators' => [
[
'name' => 'StringLength',
'options' => [
'encoding' => 'UTF-8',
'min' => 3,
'max' => 160
]
]
]
],
// other inputs to filter
];
}
defines all the inputs to be validated. With this implementation, I don't have to set the filter explicitly in the controller, just call $this->form->isValid() and magic happens. :-)

Categories