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())
{
}
}
Related
In CodeIgniter 3 was ability to get all language lines as array with:
$this->lang->language
It is very useful for views - no need to list all the required language fields and just add all of them (from the loaded language files).
Is it possible to do that in CodeIgniter 4?
Here is my Solution.
Create an App\Libraries Class and extends Language.
App\Libraries\I18n.php
<?php
namespace App\Libraries;
use CodeIgniter\Language\Language;
class I18n extends Language {
public function GetAllLanguage($with_class = false) {
$result = array();
$lang_arr = $this->language[$this->locale];
if ($with_class == true) {
return $lang_arr;
}
foreach ($lang_arr as $key => $value) {
$result += $lang_arr[$key];
}
return $result;
}
}
you can add method or something in this Libraries Class.
then Add this code in app\Config\Services.php
<?php
namespace Config;
use CodeIgniter\Config\BaseService;
use Config\Services as AppServices;
use Locale;
class Services extends BaseService
{
//override Language
//source code from system\Config\Services.php
public static function Language($locale = null, $getShared = true) {
if ($getShared) {
return static::getSharedInstance('language', $locale)->setLocale($locale);
}
if (AppServices::request() instanceof IncomingRequest) {
$requestLocale = AppServices::request()->getLocale();
} else {
$requestLocale = Locale::getDefault();
}
// Use '?:' for empty string check
$locale = $locale ?: $requestLocale;
return new \App\Libraries\I18n($locale); //override use Libraries
}
}
then you can use in controller.
when I have two Language file.
( Language\zh-TW\Menu.php 、 Language\zh-TW\Text.php)
$lang = \Config\Services::Language();
$lang->GetAllLanguage();
/**
array(157) {
["Login"] => "登入",
["BaseData"] => "基本資料",
...
["refresh"] => "刷新",
["AccountInfo"] => "使用者資訊",
["system_msg"] => "系統訊息",
...
}
**/
$lang = \Config\Services::Language();
$lang->GetAllLanguage(true);
/**
array(2) {
["Menu"]=>
array(26) {
["Login"] => "登入",
["BaseData"] => "基本資料"
...
},
["Text"]=>
array(131) {
["refresh"] => "刷新",
["AccountInfo"] => "使用者資訊",
["system_msg"] => "系統訊息",
...
}
}
**/
that it work.
I'm migrating the Zend\Db driven DBAL of a Zend Framework 3 application to Doctrine. Everything is working fine, but now I got a problem with the export of data.
Before the migration it was working as follows:
There is a more or less complex data structure. The Mapper executed some database requests and built a nested DataObject from this data. So, the start point for the export was an object, filled with all data and having sub-objects, also with all their data. So I simply converted it to JSON:
public function exportToJson(AbstractDataObject $dataObject)
{
return json_encode($dataObject, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function exportToXml(AbstractDataObject $dataObject)
{
$dataObjectVars = json_decode(json_encode($dataObject->jsonSerialize()), true);
$xml = new \SimpleXMLElement('<' . self::XML_DEFAULT_ROOT_ELEMENT . ' />');
$this->arrayToXml($dataObjectVars, $xml);
$domxml = new \DOMDocument('1.0');
$domxml->preserveWhiteSpace = false;
$domxml->formatOutput = true;
$domxml->loadXML($xml->asXML());
$xmlString = $domxml->saveXML();
return $xmlString;
}
protected function arrayToXml($array, &$xml)
{
foreach ($array as $key => $value) {
if(is_array($value)){
if(is_int($key)){
$key = self::XML_DEFAULT_ELEMENT_NAME;
}
$label = $xml->addChild($key);
$this->arrayToXml($value, $label);
}
else {
$xml->addChild($key, $value);
}
}
}
All DataObjects extended the AbstractDataObject and it provided a method, that made it easily exportable to JSON:
class AbstractDataObject implements \JsonSerializable
{
public function jsonSerialize()
{
$reflection = new \ReflectionClass($this);
$properties = $reflection->getProperties();
$members = [];
foreach ($properties as $property) {
$property->setAccessible(true);
$members[$property->getName()] = $property->getValue($this);
}
$keys = array_keys($members);
$values = array_values($members);
$keysUnderscored = preg_replace_callback('/([A-Z])/', function($matches) {
return '_' . strtolower($matches[1]);
}, $keys);
$varsUnderscored = array_combine($keysUnderscored, $values);
return $varsUnderscored;
}
}
Now the object to export is an entity and it usually doesn't not have all its data loaded. That means, the approach described above doesn't work anymore.
Is there / What is a proper way to convert a nested entity (means an entity with its sub-entities) to a structured data format (array / JSON / XML)?
Finally I've got it working as suggested in Cerad's comment with the Symfony Serializer Component.
I got some troubles with the encoding: The JSON_ERROR_UTF8 for JSON and the "Warning: DOMDocument::saveXML(): invalid character value" for XML. So I had to "utf8ize" the array data received from the Serializer and reimplement the exportToXml(...) by using the dom_import_simplexml(...) as shown here. But now it's working, here we go:
namespace MyNamespace\DataExport;
use MyNamespace\DataObject\AbstractDataObject;
use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
class DataExporter
{
/** #var string */
const EXPORT_FORMAT_JSON = 'json';
/** #var string */
const EXPORT_FORMAT_XML = 'xml';
/** #var string */
const XML_DEFAULT_ROOT_ELEMENT = 'my_root_element_name';
/** #var string */
const XML_DEFAULT_ELEMENT_NAME = 'item';
/** #var Serializer */
protected $serializer;
public function __construct()
{
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory, new CamelCaseToSnakeCaseNameConverter());
$normalizer->setCircularReferenceLimit(1);
$normalizer->setIgnoredAttributes(['__initializer__', '__cloner__', '__isInitialized__']);
$normalizer->setCircularReferenceHandler(function ($object) {
// #todo A cleaner solution need.
try {
$return = $object->getId();
} catch (\Error $exception) {
$return = null;
}
$return = null;
return $return;
});
$normalizers = [$normalizer];
$this->serializer = new Serializer($normalizers);
}
public function exportToJson(AbstractDataObject $dataObject)
{
$data = $this->serializer->normalize($dataObject, null, ['groups' => ['export']]);
$data = $this->utf8ize($data);
return json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
}
public function exportToXml(AbstractDataObject $dataObject)
{
$data = $this->serializer->normalize($dataObject, null, ['groups' => ['export']]);
$data = $this->utf8ize($data);
$xml = new \SimpleXMLElement('<' . self::XML_DEFAULT_ROOT_ELEMENT . ' />');
$this->arrayToXml($data, $xml);
$domDocument = dom_import_simplexml($xml)->ownerDocument;
$domDocument->formatOutput = true;
$xmlString = $domDocument->saveXML();
return $xmlString;
}
protected function utf8ize($data) {
if (is_array($data)) {
foreach ($data as $key => $value) {
$data[$key] = $this->utf8ize($value);
}
} else if (is_string ($data)) {
return utf8_encode($data);
}
return $data;
}
/**
* Converts an $array to XML and
* saves the result to the $xml argument.
*
* #param array $array
* #param \SimpleXMLElement $xml
* #return void
*/
protected function arrayToXml($array, &$xml){
foreach ($array as $key => $value) {
if(is_array($value)){
if(is_int($key)){
$key = self::XML_DEFAULT_ELEMENT_NAME;
}
$label = $xml->addChild($key);
$this->arrayToXml($value, $label);
}
else {
$xml->addChild($key, $value);
}
}
}
}
I'm calling a class by a string variable passed on a function argument.
ApiTester.php
use MyApp\Sites\Site;
abstract class ApiTester extends TestCase() {
/**
* Make a new record in the DB
*
* #param $type
* #param array $fields
* #throws BadMethodCallException
*/
protected function make($type, array $fields = [])
{
while($this->times--)
{
$stub = array_merge($this->getStub(), $fields);
$type::create($stub);
}
}
SitesTester.php
class SitesTester extends ApiTester() {
/** #test */
public function it_fetches_a_single_site()
{
// arrange
$this->make('Site');
// act
$site = $this->getJson('api/v1/sites/1')->data;
// assertion
$this->assertResponseOk();
$this->assertObjectHasAttributes($site, 'name', 'address');
}
Site.php // Eloquent Model
namespace MyApp\Sites;
class Site extends \Eloquent {
}
But if I call the class that the string variable $type contains, for example; string variable $type contains 'Site', it says class 'Site' not found.
I tried to manually type Site::create($stub) and finally accepts it.
I also tried
call_user_func($type::create(), $stub);
and
$model = new $type;
$model->create($stub);
but unfortunately it says class 'Site' not found.
Any ideas?
You're almost there:
class X {
static function foo($arg) {
return 'hi ' . $arg;
}
};
$cls = 'X';
print call_user_func("$cls::foo", 'there');
If your php is very old (<5.3 I believe), you have to use an array instead:
print call_user_func(array($cls, "foo"), 'there');
You may want to replace that static class call with the following :
while( $this->times-- )
{
$stub = array_merge( $this->getStub(), $fields );
call_user_func( "$type::create", $stub );
}
Runnable code here : http://runnable.com/VIqy4CDePeY-AeMV/output
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.
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