ZF2 - Creating custom form view helpers - php

Some time ago, Matthew Weier O'Phinney posted this article on his blog about creating composite form elements in Zend Framework 1.
I'm trying to create the same element for my custom library in Zend Framewor 2 but I'm having issues finding the form view helper when rendering the form.
Here is my element (DateSegmented.php):
<?php
namespace Si\Form\Element;
use Zend\Form\Element;
use Zend\ModuleManager\Feature\ViewHelperProviderInterface;
class DateSegmented extends Element implements ViewHelperProviderInterface
{
public function getViewHelperConfig(){
return array( 'type' => '\Si\Form\View\Helper\DateSegment' );
}
protected $_dateFormat = '%year%-%month%-%day%';
protected $_day;
protected $_month;
protected $_year;
/**
* Seed attributes
*
* #var array
*/
protected $attributes = array(
'type' => 'datesegmented',
);
public function setDay($value)
{
$this->_day = (int) $value;
return $this;
}
public function getDay()
{
return $this->_day;
}
public function setMonth($value)
{
$this->_month = (int) $value;
return $this;
}
public function getMonth()
{
return $this->_month;
}
public function setYear($value)
{
$this->_year = (int) $value;
return $this;
}
public function getYear()
{
return $this->_year;
}
public function setValue($value)
{
if (is_int($value)) {
$this->setDay(date('d', $value))
->setMonth(date('m', $value))
->setYear(date('Y', $value));
} elseif (is_string($value)) {
$date = strtotime($value);
$this->setDay(date('d', $date))
->setMonth(date('m', $date))
->setYear(date('Y', $date));
} elseif (is_array($value)
&& (isset($value['day'])
&& isset($value['month'])
&& isset($value['year'])
)
) {
$this->setDay($value['day'])
->setMonth($value['month'])
->setYear($value['year']);
} else {
throw new Exception('Invalid date value provided');
}
return $this;
}
public function getValue()
{
return str_replace(
array('%year%', '%month%', '%day%'),
array($this->getYear(), $this->getMonth(), $this->getDay()),
$this->_dateFormat
);
}
}
And here is my form view helper:
<?php
namespace Si\Form\View\Helper;
use Zend\Form\ElementInterface;
use Si\Form\Element\DateSegmented as DateSegmented;
use Zend\Form\Exception;
class DateSegmented extends FormInput
{
/**
* Render a form <input> element from the provided $element
*
* #param ElementInterface $element
* #throws Exception\InvalidArgumentException
* #throws Exception\DomainException
* #return string
*/
public function render(ElementInterface $element)
{
$content = "";
if (!$element instanceof DateSegmented) {
throw new Exception\InvalidArgumentException(sprintf(
'%s requires that the element is of type Si\Form\Input\DateSegmented',
__METHOD__
));
}
$name = $element->getName();
if (empty($name) && $name !== 0) {
throw new Exception\DomainException(sprintf(
'%s requires that the element has an assigned name; none discovered',
__METHOD__
));
}
$view = $element->getView();
if (!$view instanceof \Zend\View\View) {
// using view helpers, so do nothing if no view present
return $content;
}
$day = $element->getDay();
$month = $element->getMonth();
$year = $element->getYear();
$name = $element->getFullyQualifiedName();
$params = array(
'size' => 2,
'maxlength' => 2,
);
$yearParams = array(
'size' => 4,
'maxlength' => 4,
);
$markup = $view->formText($name . '[day]', $day, $params)
. ' / ' . $view->formText($name . '[month]', $month, $params)
. ' / ' . $view->formText($name . '[year]', $year, $yearParams);
switch ($this->getPlacement()) {
case self::PREPEND:
return $markup . $this->getSeparator() . $content;
case self::APPEND:
default:
return $content . $this->getSeparator() . $markup;
}
$attributes = $element->getAttributes();
$attributes['name'] = $name;
$attributes['type'] = $this->getInputType();
$attributes['value'] = $element->getCheckedValue();
$closingBracket = $this->getInlineClosingBracket();
if ($element->isChecked()) {
$attributes['checked'] = 'checked';
}
$rendered = sprintf(
'<input %s%s',
$this->createAttributesString($attributes),
$closingBracket
);
if ($element->useHiddenElement()) {
$hiddenAttributes = array(
'name' => $attributes['name'],
'value' => $element->getUncheckedValue(),
);
$rendered = sprintf(
'<input type="hidden" %s%s',
$this->createAttributesString($hiddenAttributes),
$closingBracket
) . $rendered;
}
return $rendered;
}
/**
* Return input type
*
* #return string
*/
protected function getInputType()
{
return 'datesegmented';
}
}
This question describes adding the view helper as an invokable, but it's already being declared, as my custom library (Si) has been added to the 'StandardAutoLoader'.

OK, figured this one out eventually.
Copy Zend/Form/View/HelperConfig.php to the same location in your custom library. Adjust contents to reflect your view helpers.
Add the following to an event or bootstrap in your Module.php
$app = $e->getApplication();
$serviceManager = $app->getServiceManager();
$phpRenderer = $serviceManager->get('ViewRenderer');
$plugins = $phpRenderer->getHelperPluginManager();
$config = new \Si\Form\View\HelperConfig;
$config->configureServiceManager($plugins);
Update the 'Si' namespace with your custom one.
The 'class already exists' error was actually down to the includes at the top of my view helper file. I have updated it with:
use Zend\Form\View\Helper\FormElement;
use Zend\Form\Element;
use Zend\Form\ElementInterface;
use Zend\Form\Exception;
I also updated the instanceof statement to an absolute location due to the duplicate class names:
if (!$element instanceof \Si\Form\Element\DateSegmented) {
There were further errors in the translation from ZF1 to 2 but they are not related to this issue.

The way i understand your code is: You are creating a new Form\Element as well as a new Form\View\Helper. In this case the following information is needed for you:
The StandardAutoloader only takes care of actually finding the classes. The declaration of the invokables inside the getViewHelperConfig() is there, so the framework knows what Class to load when the ViewHelper is called.
In your case you'd do it like this:
public function getViewHelperConfig()
{
return array(
'invokables' => array(
'dateSegmented' => 'Si\Form\View\Helper\DateSegmented'
)
);
}
Zend Framework 2 does this for it's own ViewHelpers inside /Zend/Form/View/HelperConfig.php

Related

ZF2 Get params in factory

I have a dynamic category navigation. In the navigation factory I want to get a param from the route. How can I do this?
In my view
<?php echo $this->navigation('CategoryNavigation')->menu()->setUlClass('list-group'); ?>
In my module.php:
public function getServiceConfig()
{
return array(
'factories' => array(
'CategoryNavigation' => 'Application\Navigation\CategoryNavigation',
)
);
}
This is my navigation factory. I need to get the slug from the route where {{ store slug }} (2x) is defined.
<?php
namespace Application\Navigation;
use Zend\ServiceManager\ServiceLocator;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\Navigation\Service\DefaultNavigationFactory;
class CategoryNavigation extends DefaultNavigationFactory
{
protected $sl;
protected function getPages(ServiceLocatorInterface $serviceLocator)
{
if (null === $this->pages) {
$em = $serviceLocator->get('Doctrine\ORM\EntityManager');
$storerepository = $em->getRepository('ApplicationShared\Entity\Store');
$store = $storerepository->findOneBy(array('slug' => '{{ store slug }}'));
$fetchMenu = $store->getCategories();
$configuration['navigation'][$this->getName()] = $this->buildNavigation($fetchMenu);
if (!isset($configuration['navigation'])) {
throw new Exception\InvalidArgumentException('Could not find navigation configuration key');
}
if (!isset($configuration['navigation'][$this->getName()])) {
throw new Exception\InvalidArgumentException(sprintf(
'Failed to find a navigation container by the name "%s"',
$this->getName()
));
}
$application = $serviceLocator->get('Application');
$routeMatch = $application->getMvcEvent()->getRouteMatch();
$router = $application->getMvcEvent()->getRouter();
$pages = $this->getPagesFromConfig($configuration['navigation'][$this->getName()]);
//
$this->pages = $this->injectComponents($pages, $routeMatch, $router);
}
return $this->pages;
}
protected function buildNavigation($elements, $parentid = null){
foreach ($elements as $index => $element) {
if (!$element->getParent()) {
$branch[$element->getCategoryName()] = $this->navigationItem($element);
}
}
return $branch;
}
protected function navigationItem($element){
$branch['label'] = $element->getCategoryName();
$branch['route'] = 'store/type/catalog';
$branch['params'] = array('slug' => '{{ store slug }}','category' => $element->getSlug());
if(count($element->getChildren()) > 0){
foreach($element->getChildren() as $element){
$branch['pages'][] = $this->navigationItem($element);
}
}
return $branch;
}
}
Or are there better way's to achieve this?
Searching for a few hours, in the end asking it on stack, and now I find the answer in a half hour.
I just had to ad this in my factory
$router = $serviceLocator->get('router');
$request = $serviceLocator->get('request');
// Get the router match
$routerMatch = $router->match($request);
$this->slug = $routerMatch->getParam("slug");
Or the following for ZF3:
$container->get('Application')->getMvcEvent()->getRouteMatch()->getParam('whateverYouWant', false);

zend framework 2 + custom routing

I tried to follow the recommendations from this topic: zend framework 2 + routing database
I have a route class:
namespace Application\Router;
use Zend\Mvc\Router\Http\RouteInterface;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\Mvc\Router\RouteMatch;
class Content implements RouteInterface, ServiceLocatorAwareInterface {
protected $defaults = array();
protected $routerPluginManager = null;
public function __construct(array $defaults = array()) {
$this->defaults = $defaults;
}
public function setServiceLocator(\Zend\ServiceManager\ServiceLocatorInterface $routerPluginManager) {
$this->routerPluginManager = $routerPluginManager;
}
public function getServiceLocator() {
return $this->routerPluginManager;
}
public static function factory($options = array()) {
if ($options instanceof \Traversable) {
$options = ArrayUtils::iteratorToArray($options);
} elseif (!is_array($options)) {
throw new InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options');
}
if (!isset($options['defaults'])) {
$options['defaults'] = array();
}
return new static($options['defaults']);
}
public function match(Request $request, $pathOffset = null) {
if (!method_exists($request, 'getUri')) {
return null;
}
$uri = $request->getUri();
$fullPath = $uri->getPath();
$path = substr($fullPath, $pathOffset);
$alias = trim($path, '/');
$options = $this->defaults;
$options = array_merge($options, array(
'path' => $alias
));
return new RouteMatch($options);
}
public function assemble(array $params = array(), array $options = array()) {
if (array_key_exists('path', $params)) {
return '/' . $params['path'];
}
return '/';
}
public function getAssembledParams() {
return array();
}
}
Pay attention that the match() function returns object of the instance of Zend\Mvc\Router\RouteMatch
However in the file Zend\Mvc\Router\Http\TreeRouteStack it checks for object to be the instance of RouteMatch (without prefix of namespace)
if (
($match = $route->match($request, $baseUrlLength, $options)) instanceof RouteMatch
&& ($pathLength === null || $match->getLength() === $pathLength)
)
And the condition fails in my case because of the namespace.
Any suggestions?
Ok, i figured out what the problem was.
Instead of returning Zend\Mvc\Router\RouteMatch I should return Zend\Mvc\Router\Http\RouteMatch
This fixed my problem

ZF2 ServiceLocatorAwareInterface getServiceLocator gives me the Zend\Validator\ValidatorPluginManager

For my ZF2 form validation I need the entity manager in my custom validator. I Zend\ServiceManager\ServiceLocatorAwareInterface to get the service locator in my validator so I would be able to get the entitymanager from there.
My problem is that this Zend\ServiceManager\ServiceLocatorAwareInterface::setServiceLocator injects the "Zend\Validator\ValidatorPluginManager" and not the desired ServiceLocator.
Does anybody know how to solve this problem?
My abstract controller
abstract class AbstractController extends AbstractActionController
{
/**
* This property contains the doctrine entitymanager.
* #var Doctrine\ORM\EntityManager
*/
protected $_entityManager;
/**
* This method returns the doctrine entitymanager.
* #return \Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
var_dump(spl_object_hash($this->getServiceLocator())); echo '<br>';
if (null === $this->_entityManager)
$this->_entityManager = $this->getServiceLocator()->get('doctrine.entitymanager.orm_default');
return $this->_entityManager;
}
}
My validator:
class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
protected $_serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
var_dump(spl_object_hash($serviceLocator)); echo '<br>';
$this->_serviceLocator = $serviceLocator;
}
function getServiceLocator()
{
return $this->_serviceLocator;
}
}
When executing this both var_dumps result in another object hash:
string(32) "000000005e4258390000000024a7f829"
string(32) "000000005e425a2d0000000024a7f829"
The reason for this is that in your validator you are getting the Zend\Validator\ValidatorPluginManager injected. If you want the the Service Manager call getServiceLocator on the Zend\Validator\ValidatorPluginManager.
For example look at the someMethod() method bellow:
class EntityUnique extends AbstractValidator implements ServiceLocatorAwareInterface
{
protected $_serviceLocator;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
var_dump(spl_object_hash($serviceLocator)); echo '<br>';
$this->_serviceLocator = $serviceLocator;
}
public function getServiceLocator()
{
return $this->_serviceLocator;
}
public function someMethod()
{
$validatorPluginManager = $this->getServiceLocator();
$serviceLocator = $validatorPluginManager->getServiceLocator(); // HERE
}
}
Hope this helps :)
Stoyan
I got it working in the end. I don't know if this is the best practice so if you have any feedback don't hesitate. My validator ended up like this:
<?php
namespace Flex\Validator;
use Doctrine\ORM\EntityManager;
use Zend\Stdlib\ArrayUtils;
use Zend\ServiceManager\ServiceManager;
use Zend\Validator\AbstractValidator;
class EntityUnique extends AbstractValidator
{
const EXISTS = 'exists';
protected $messageTemplates = array(
self::EXISTS => 'A %entity% entity allready exists.',
);
protected $messageVariables = array(
'entity' => '_entity',
);
protected $_context;
protected $_entity;
protected $_excludes = array();
protected $_filters = array();
protected $_property;
protected $_serviceLocator;
public function __construct($options = null)
{
if ($options instanceof \Traversable)
$options = ArrayUtils::iteratorToArray($options);
if (is_array($options))
{
if (isset($options['entity']))
$this->_entity = $options['entity'];
if (isset($options['exclude']))
{
if (!is_array($options['exclude']))
throw new \Exception('Exclude option should be an array');
if (isset($options['exclude']['property']) && isset($options['exclude']['value']))
$this->_excludes[] = array(
'property' => $options['exclude']['property'],
'value' => $options['exclude']['value'],
);
else
foreach ($options['exclude'] as $exclude)
{
if (!isset($exclude['property']) || !isset($exclude['value']))
throw new \Exception('exclude shoud contain a property and value');
$this->_excludes[] = array(
'property' => $exclude['property'],
'value' => $exclude['value'],
);
}
}
if (isset($options['filter']))
{
if (!is_array($options['filter']))
throw new \Exception('Filter option should be an array');
if (isset($options['filter']['property']) && isset($options['filter']['value']))
$this->_filters[] = array(
'property' => $options['filter']['property'],
'value' => $options['filter']['value'],
);
else
foreach ($options['filter'] as $filter)
{
if (!isset($filter['property']) || !isset($filter['value']))
throw new \Exception('Filters shoud contain a property and value');
$this->_filters[] = array(
'property' => $filter['property'],
'value' => $filter['value'],
);
}
}
if (isset($options['property']))
$this->_property = $options['property'];
if (!isset($options['serviceLocator']))
throw new \InvalidArgumentException(__CLASS__ . ' requires the option serviceLocator.');
$ServiceManagerInstance = 'Zend\ServiceManager\ServiceManager';
if (!($options['serviceLocator'] instanceof $ServiceManagerInstance))
throw new \InvalidArgumentException(__CLASS__ . ' expects the option serviceLocator to be an instance of Zend\ServiceManager\ServiceManager.');
$this->_serviceLocator = $options['serviceLocator'];
}
parent::__construct(is_array($options) ? $options : null);
}
public function isValid($value, $context = null)
{
$this->setValue($value);
$this->_context = $context;
$entityManager = $this->_serviceLocator->get('doctrine.entitymanager.orm_default');
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->from($this->_entity, 'e')
->select('COUNT(e)')
->where('e.' . $this->_property . ' = :value')
->setParameter('value', $this->getValue());
if (count($this->_excludes))
foreach ($this->_excludes as $key => $exclude)
{
$exclude['value'] = $this->parse($exclude['value']);
if ($exclude['value'] !== null)
$queryBuilder->andWhere('e.' . $exclude['property'] . ' = :exclude_' . $key)
->setParameter('exclude_' . $key, $exclude['value']);
else
$queryBuilder->andWhere('e.' . $exclude['property'] . ' IS NULL');
}
if (count($this->_filters))
foreach ($this->_filters as $key => $filter)
{
$filter['value'] = $this->parse($filter['value']);
if ($filter['value'] !== null)
$queryBuilder->andWhere('e.' . $filter['property'] . ' = :filter_' . $key)
->setParameter('filter_' . $key, $filter['value']);
else
$queryBuilder->andWhere('e.' . $filter['property'] . ' IS NULL');
}
$query = $queryBuilder->getQuery();
if ((integer) $query->getSingleScalarResult() !== 0)
{
$this->error(self::EXISTS);
return false;
}
return true;
}
private function parse($input, $current = null)
{
if (!is_array($input))
return $input;
if ($current === null)
$current = $this;
$currentKey = array_shift($input);
if (is_object($current) && property_exists($current, $currentKey))
$current = $current->$currentKey;
elseif (is_array($current) && array_key_exists($currentKey, $current))
$current = $current[$currentKey];
else
return null;
if (count($input))
return $this->parse($input, $current);
if (strlen($current) == 0)
return null;
return $current;
}
}
This is loaded in like this:
<?php
namespace FlexCategories\Form;
use Zend\Form\Form;
use Zend\InputFilter\InputFilterProviderInterface;
use Zend\ServiceManager\ServiceManager;
use Zend\ServiceManager\ServiceManagerAwareInterface;
class CategoryForm extends Form implements InputFilterProviderInterface, ServiceManagerAwareInterface
{
private $_serviceManager;
public function init()
{
/* */
}
public function getInputFilterSpecification()
{
return array(
'name' => array(
'filters' => array(
array(
'name' => 'Zend\Filter\StringTrim'
),
),
'required' => true,
'validators' => array(
array(
'name' => 'Flex\Validator\EntityUnique',
'options' => array(
'entity' => 'FlexCategories\Entity\Category',
'filter' => array(
array('property' => 'parent',
'value' => array('_context', 'parent')),
),
'property' => 'name',
'serviceLocator' => $this->_serviceManager,
),
),
),
),
);
}
public function setServiceManager(ServiceManager $serviceManager)
{
$this->_serviceManager = $serviceManager;
$this->init();
return $this;
}
}
The form is registered in the service_manager => invokables and is retreived within the controller thorugh the service manager.
This worked for me. I hope somebody will be able to learn from this or give me feedbacl to create an better solution.

Overriding DoctrineExtensions method in StofDoctrineExtensions

I am trying to change the behaviour of the Gedmo\Tree\RepositoryUtils->buildTree() method because I'd like to change the way the returned array is constructed.
I am trying to following:
I have a class:
<?php
namespace MyCorp\CMSBundle\Util;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Exception\InvalidArgumentException;
/**
* Description of jsandjqTreeCompatibleRepositoryUtils
*
* #author peterrus
*/
class jsandjqTreeCompatibleRepositoryUtils extends Gedmo\Tree\RepositoryUtils {
public function buildTree(array $nodes, array $options = array()) {
$meta = $this->getClassMetadata();
$nestedTree = $this->repo->buildTreeArray($nodes);
$default = array(
'decorate' => false,
'rootOpen' => '<ul>',
'rootClose' => '</ul>',
'childOpen' => '<li>',
'childClose' => '</li>',
'nodeDecorator' => function ($node) use ($meta) {
// override and change it, guessing which field to use
if ($meta->hasField('title')) {
$field = 'title';
} elseif ($meta->hasField('name')) {
$field = 'name';
} else {
throw new InvalidArgumentException("Cannot find any representation field");
}
return $node[$field];
}
);
$options = array_merge($default, $options);
// If you don't want any html output it will return the nested array
if (!$options['decorate']) {
return $nestedTree;
}
if (!count($nestedTree)) {
return '';
}
$build = function($tree) use (&$build, &$options) {
$output = is_string($options['rootOpen']) ? $options['rootOpen'] : $options['rootOpen']($tree);
foreach ($tree as $node) {
$output .= is_string($options['childOpen']) ? $options['childOpen'] : $options['childOpen']($node);
$output .= $options['nodeDecorator']($node);
if (count($node['children']) > 0) {
$output .= $build($node['children']);
}
$output .= $options['childClose'];
}
return $output . $options['rootClose'];
};
return $build($nestedTree);
}
}
?>
Now I am trying to use this class instead of the one that is used by default when calling
$pagerepo = $this->getDoctrine()->getRepository('MyCorpCMSBundle:Page');
By doing the following typecasting:
$pagerepo = (jsandjqTreeCompatibleRepositoryUtils) $this->getDoctrine()->getRepository('MyCorpCMSBundle:Page');
But as this is no java, this is not possible.
What am I doing wrong?
May be a little late, but i needed to to the same thing - here is the solution in case someone else needs it:
Your Repository Class:
namespace Acme\Model\Repository;
use Doctrine\ORM\EntityManager;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
use Doctrine\ORM\Mapping\ClassMetadata;
use MyNamespace\GenericBundle\Repository\RepositoryUtils as MyRepositoryUtils;
/**
* Group Repository
*/
class CategoryRepository extends NestedTreeRepository
{
/**
* #param EntityManager $em
* #param ClassMetadata $class
*/
public function __construct(EntityManager $em, ClassMetadata $class)
{
parent::__construct($em, $class);
$this->repoUtils = new MyRepositoryUtils($this->_em, $this->getClassMetadata(), $this->listener, $this);
}
}
in the MyRepositoryUtils you can overwrite the buildTree method:
namespace MyNamespace\GenericBundle\Repository;
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
use Doctrine\Common\Persistence\ObjectManager;
use Gedmo\Exception\InvalidArgumentException;
use Gedmo\Tree\RepositoryUtils as GedmoRepositoryUtils;
class RepositoryUtils extends GedmoRepositoryUtils
{
/**
* {#inheritDoc}
*/
public function buildTree(array $nodes, array $options = array())
{
}
}

Zend Framework validation and/or filtering issue

I am having a problem with the filtering and validation of the form below. If the two mobile numbers are entered in national format (07777654321) the form validation fails, but if they are entered in international format (447777654321) it validates. My understanding is that the filters will be applied first so when identical is applied both mobile and mobile_confirm should have been processed with the same set of filters.
So if I enter 07777654321 into both fields it will get filtered to 447777654321 in both fields which should then validate. Where am I going wrong.
I am using ZF 1.11.3.
<?php
class Test_Form_Check extends Zend_Form
{
public function init()
{
$mobile= new Zend_Form_Element_Text( 'mobile' );
$mobile->addFilter(new Test_Filter_MobileFilter('44'))
->addValidator(new Test_Validate_Mobile())
->setRequired( true );
$mobile1= new Zend_Form_Element_Text( 'mobile_confirm' );
$mobile1->addFilter(new Test_Filter_MobileFilter('44'))
->addValidator(new Test_Validate_Mobile())
->addValidator('Identical', false, array('token' => 'mobile', 'messages' => 'Mobile numbers do not match.'))
->setRequired( true );
$Add = new Zend_Form_Element_Submit('Add');
$Add->setLabel('Submit');
$this->addElement($mobile)
->addElement($mobile1)
->addElement( $Add );
}
}
class Test_Validate_Mobile extends Zend_Validate_Abstract
{
const NOT_DIGITS = 'notDigits';
const STRING_EMPTY = 'digitsStringEmpty';
const INVALID = 'digitsInvalid';
const INVALIDPHONE = 'phonenumberinvalid';
protected static $_filter = null;
protected $_messageTemplates = array(
self::NOT_DIGITS => "'%value%' must contain only digits",
self::STRING_EMPTY => "'%value%' is an empty string",
self::INVALID => "Invalid type given. String, integer or float expected",
self::INVALIDPHONE => "Invalid number, try with country code",
);
public function isValid($value)
{
if (!is_string($value) && !is_int($value) && !is_float($value)) {
$this->_error(self::INVALID);
return false;
}
if (!preg_match('/^(447)[0-9]{9}$/', $value)) {
$this->_error(self::INVALIDPHONE);
return false;
}
$this->_setValue((string) $value);
if ('' === $this->_value) {
$this->_error(self::STRING_EMPTY);
return false;
}
if (null === self::$_filter) {
require_once 'Zend/Filter/Digits.php';
self::$_filter = new Zend_Filter_Digits();
}
if ($this->_value !== self::$_filter->filter($this->_value)) {
$this->_error(self::NOT_DIGITS);
return false;
}
return true;
}
}
class Test_Filter_MobileFilter implements Zend_Filter_Interface
{
/**
* Default country code
*
* #var string
*/
protected $_def_country = '44';
public function __construct($options = null)
{
if ($options !== null) {
if (is_string($options) && is_numeric($options)) {
$this->_def_country = $options;
}
else {
require_once 'Zend/Filter/Exception.php';
throw new Zend_Filter_Exception('Options not valid country code');
}
}
}
public function filter($value)
{
if (!empty($value)) {
$replace = array(' ','+','-','(',')');
$value = str_replace($replace, '', $value);
$value = preg_replace('/\A0/', $this->_def_country, $value);
}
return $value;
}
}
I will be very grateful for any help with this. It is driving me round the bend.
I guess problem is with Zend_Validate_Identical validator , it uses unfiltered raw value from $_POST. Just quick fix:
class My_Validate_Identical extends Zend_Validate_Identical
{
/**
*
* #var Zend_Form_Element
*/
protected $_element = null;
public function __construct($token = null)
{
if (is_array($token) && array_key_exists('element', $token)) {
$this->_element = $token['element'];
}
parent::__construct($token);
}
public function isValid($value, $context = null)
{
$context[$this->getToken()] = $this->_element->getValue();
return parent::isValid($value, $context);
}
}
Attaching validator:
$mobile1->addValidator(new My_Validate_Identical(array(
'token' => 'mobile',
'messages' => 'Mobile numbers do not match.',
'element' => $mobile
)))
Hope this helps :)

Categories