Last active
September 1, 2016 13:34
-
-
Save rogatec/a36c599bd5b8fe4d2c0a67cbb63f5e93 to your computer and use it in GitHub Desktop.
Zend Framework 3 get model db result with foreign keys to another/itself model with custom hydrator and form fieldsets (in progress)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Model; | |
class Bank | |
{ | |
public $id; | |
public $name; | |
public $bic; | |
/** | |
* @param array $data | |
* @param string $prefix if you need only a getBankById you don't have a prefix | |
* in the employee relation with the 'bank.' prefix you can create your bank object this way | |
*/ | |
public function exchangeArray(array $data, $prefix = '') | |
{ | |
$this->id = !empty($data[$prefix.'id']) ? $data[$prefix.'id'] : null; | |
$this->name = !empty($data[$prefix.'name']) ? $data[$prefix.'name'] : null; | |
$this->bic = !empty($data[$prefix.'bic']) ? $data[$prefix.'bic'] : null; | |
} | |
public function getArrayCopy() | |
{ | |
return get_object_vars($this); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Form\Fieldset; | |
use Employee\Model\Bank; | |
use Employee\Model\BankTable; | |
use Zend\Form\Element\Select; | |
use Zend\Form\Fieldset; | |
use Zend\InputFilter\InputFilterProviderInterface; | |
use Zend\Validator\NotEmpty; | |
class BankFieldset extends Fieldset implements InputFilterProviderInterface | |
{ | |
/** | |
* @var array | |
*/ | |
private $banks = []; | |
/** | |
* BankFieldset constructor. | |
* | |
* @param BankTable $bankTable | |
*/ | |
public function __construct(BankTable $bankTable) | |
{ | |
parent::__construct('bank'); | |
$resultSet = $bankTable->fetchAll(); | |
/** @var Bank $bank */ | |
foreach ($resultSet as $bank) { | |
$this->banks[$bank->id] = $bank->name . ' - [' . $bank->bic . ']'; | |
} | |
} | |
public function init() | |
{ | |
$this->add([ | |
'type' => Select::class, | |
'name' => 'description', | |
'options' => [ | |
'label' => 'Bank', | |
'label_attributes' => [ | |
'class' => 'control-label', | |
], | |
'empty_option' => 'Please select...', | |
'value_options' => $this->banks, | |
], | |
'attributes' => [ | |
'class' => 'form-control', | |
], | |
]); | |
} | |
/** | |
* Should return an array specification compatible with | |
* {@link Zend\InputFilter\Factory::createInputFilter()}. | |
* | |
* @return array | |
*/ | |
public function getInputFilterSpecification() | |
{ | |
return [ | |
'description' => [ | |
'required' => true, | |
'validators' => [ | |
[ | |
'name' => NotEmpty::class, | |
], | |
], | |
], | |
]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Factory; | |
use Employee\Form\Fieldset\BankFieldset; | |
use Employee\Model\Bank; | |
use Employee\Model\BankTable; | |
use Interop\Container\ContainerInterface; | |
use Interop\Container\Exception\ContainerException; | |
use Zend\Hydrator\ClassMethods; | |
use Zend\ServiceManager\Exception\ServiceNotCreatedException; | |
use Zend\ServiceManager\Exception\ServiceNotFoundException; | |
use Zend\ServiceManager\Factory\FactoryInterface; | |
class BankFieldsetFactory implements FactoryInterface | |
{ | |
/** | |
* Create an object | |
* | |
* @param ContainerInterface $container | |
* @param string $requestedName | |
* @param null|array $options | |
* | |
* @return object | |
* @throws ServiceNotFoundException if unable to resolve the service. | |
* @throws ServiceNotCreatedException if an exception is raised when | |
* creating a service. | |
* @throws ContainerException if any other error occurs | |
*/ | |
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) | |
{ | |
$fieldset = new BankFieldset($container->get(BankTable::class)); | |
$fieldset->setObject(new Bank())->setHydrator(new ClassMethods(false)); | |
return $fieldset; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Model; | |
class Employee | |
{ | |
public $id; | |
public $bank_id; | |
public $salutation; | |
public $title; | |
public $forename; | |
public $surname; | |
// any other database columns | |
/** | |
* @var Bank | |
*/ | |
private $bank; | |
// maybe other foreign key models as private properties | |
/** | |
* @param array $data | |
* @param string $prefix this prefix is useful if you have a foreign key to the model itself (example a manager_id) | |
* due to alias your manager_id result you will have something like manager.id manager.salutation etc. | |
* with this prefix you can fill your related manager employee model | |
*/ | |
public function exchangeArray(array $data, $prefix = '') | |
{ | |
$this->id = !empty($data[$prefix.'id']) ? $data[$prefix.'id'] : null; | |
$this->bank_id = !empty($data[$prefix.'bank_id']) ? $data[$prefix.'bank_id'] : null; | |
$this->salutation = !empty($data[$prefix.'salutation']) ? $data[$prefix.'salutation'] : null; | |
$this->title = !empty($data[$prefix.'title']) ? $data[$prefix.'title'] : null; | |
$this->forename = !empty($data[$prefix.'forename']) ? $data[$prefix.'forename'] : null; | |
$this->surname = !empty($data[$prefix.'surname']) ? $data[$prefix.'surname'] : null; | |
// other database columns | |
} | |
public function getArrayCopy() | |
{ | |
return get_object_vars($this); | |
} | |
/** | |
* @return Bank | |
*/ | |
public function getBank() | |
{ | |
return $this->bank; | |
} | |
/** | |
* @param Bank $bank | |
* | |
* @return Employee | |
*/ | |
public function setBank($bank) | |
{ | |
$this->bank = $bank; | |
return $this; | |
} | |
// maybe other foreign key models with getter/setter | |
// input filters for form | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Form\Fieldset; | |
use Zend\Filter\StringTrim; | |
use Zend\Filter\StripTags; | |
use Zend\Filter\ToInt; | |
use Zend\Form\Element\Hidden; | |
use Zend\Form\Element\Select; | |
use Zend\Form\Element\Text; | |
use Zend\Form\Fieldset; | |
use Zend\InputFilter\InputFilterProviderInterface; | |
use Zend\Validator\NotEmpty; | |
use Zend\Validator\StringLength; | |
class EmployeeFieldset extends Fieldset implements InputFilterProviderInterface | |
{ | |
public function init() | |
{ | |
parent::__construct('employee'); | |
$this->add([ | |
'type' => Hidden::class, | |
'name' => 'id', | |
]); | |
$this->add([ | |
'type' => Select::class, | |
'name' => 'salutation', | |
'options' => [ | |
'label' => 'Anrede', | |
'label_attributes' => [ | |
'class' => 'control-label', | |
], | |
'empty_option' => 'Bitte wählen Sie aus...', | |
'value_options' => [ | |
'Frau' => 'Frau', | |
'Herr' => 'Herr', | |
], | |
], | |
'attributes' => [ | |
'class' => 'form-control', | |
], | |
]); | |
$this->add([ | |
'type' => Text::class, | |
'name' => 'title', | |
'options' => [ | |
'label' => 'Titel', | |
'label_attributes' => [ | |
'class' => 'control-label', | |
], | |
], | |
'attributes' => [ | |
'class' => 'form-control', | |
], | |
]); | |
$this->add([ | |
'type' => Text::class, | |
'name' => 'forename', | |
'options' => [ | |
'label' => 'Vorname', | |
'label_attributes' => [ | |
'class' => 'control-label', | |
], | |
], | |
'attributes' => [ | |
'class' => 'form-control', | |
], | |
]); | |
$this->add([ | |
'type' => Text::class, | |
'name' => 'surname', | |
'options' => [ | |
'label' => 'Nachname', | |
'label_attributes' => [ | |
'class' => 'control-label', | |
], | |
], | |
'attributes' => [ | |
'class' => 'form-control', | |
], | |
]); | |
$this->add([ | |
'type' => BankFieldset::class, | |
'name' => 'bank', | |
]); | |
} | |
/** | |
* Should return an array specification compatible with | |
* {@link Zend\InputFilter\Factory::createInputFilter()}. | |
* | |
* @return array | |
*/ | |
public function getInputFilterSpecification() | |
{ | |
return [ | |
'id' => [ | |
'required' => true, | |
'filters' => [ | |
['name' => ToInt::class], | |
], | |
], | |
'salutation' => [ | |
'required' => true, | |
'validators' => [ | |
[ | |
'name' => NotEmpty::class, | |
], | |
], | |
], | |
'title' => [ | |
'required' => false, | |
'filters' => [ | |
['name' => StripTags::class], | |
['name' => StringTrim::class], | |
], | |
'validators' => [ | |
[ | |
'name' => StringLength::class, | |
'options' => [ | |
'encoding' => 'UTF-8', | |
'min' => 3, | |
'max' => 32, | |
], | |
], | |
], | |
], | |
'forename' => [ | |
'required' => true, | |
'filters' => [ | |
['name' => StripTags::class], | |
['name' => StringTrim::class], | |
], | |
'validators' => [ | |
[ | |
'name' => NotEmpty::class, | |
], | |
], | |
], | |
'surname' => [ | |
'required' => true, | |
'filters' => [ | |
['name' => StripTags::class], | |
['name' => StringTrim::class], | |
], | |
'validators' => [ | |
[ | |
'name' => NotEmpty::class, | |
], | |
], | |
], | |
]; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Factory; | |
use Employee\Form\Fieldset\EmployeeFieldset; | |
use Employee\Model\Employee; | |
use Employee\Model\Hydrator\EmployeeHydrator; | |
use Interop\Container\ContainerInterface; | |
use Interop\Container\Exception\ContainerException; | |
use Zend\Hydrator\Aggregate\AggregateHydrator; | |
use Zend\Hydrator\ClassMethods; | |
use Zend\Hydrator\ObjectProperty; | |
use Zend\ServiceManager\Exception\ServiceNotCreatedException; | |
use Zend\ServiceManager\Exception\ServiceNotFoundException; | |
use Zend\ServiceManager\Factory\FactoryInterface; | |
class EmployeeFieldsetFactory implements FactoryInterface | |
{ | |
/** | |
* Create an object | |
* | |
* @param ContainerInterface $container | |
* @param string $requestedName | |
* @param null|array $options | |
* | |
* @return object | |
* @throws ServiceNotFoundException if unable to resolve the service. | |
* @throws ServiceNotCreatedException if an exception is raised when | |
* creating a service. | |
* @throws ContainerException if any other error occurs | |
*/ | |
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) | |
{ | |
$fieldset = new EmployeeFieldset(); | |
$fieldset->setHydrator(new ObjectProperty())->setObject(new Employee()); | |
return $fieldset; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Form; | |
use Employee\Form\Fieldset\EmployeeFieldset; | |
use Zend\Form\Element\Csrf; | |
use Zend\Form\Element\Submit; | |
use Zend\Form\Form; | |
class EmployeeForm extends Form | |
{ | |
public function init() | |
{ | |
parent::__construct('create_employee'); | |
$this->add([ | |
'type' => EmployeeFieldset::class, | |
'options' => [ | |
'use_as_base_fieldset' => true, | |
], | |
]); | |
$this->add([ | |
'type' => Csrf::class, | |
'name' => 'csrf', | |
]); | |
$this->add([ | |
'type' => Submit::class, | |
'name' => 'submit', | |
'attributes' => [ | |
'value' => 'save', | |
'class' => 'btn btn-primary btn-raised', | |
], | |
]); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Factory; | |
use Employee\Form\EmployeeForm; | |
use Interop\Container\ContainerInterface; | |
use Interop\Container\Exception\ContainerException; | |
use Zend\Hydrator\ClassMethods; | |
use Zend\InputFilter\InputFilter; | |
use Zend\ServiceManager\Exception\ServiceNotCreatedException; | |
use Zend\ServiceManager\Exception\ServiceNotFoundException; | |
use Zend\ServiceManager\Factory\FactoryInterface; | |
class EmployeeFormFactory implements FactoryInterface | |
{ | |
/** | |
* Create an object | |
* | |
* @param ContainerInterface $container | |
* @param string $requestedName | |
* @param null|array $options | |
* | |
* @return object | |
* @throws ServiceNotFoundException if unable to resolve the service. | |
* @throws ServiceNotCreatedException if an exception is raised when | |
* creating a service. | |
* @throws ContainerException if any other error occurs | |
*/ | |
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) | |
{ | |
$form = new EmployeeForm(); | |
$form->setHydrator(new ClassMethods(false))->setInputFilter(new InputFilter()); | |
return $form; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Model\Hydrator; | |
use Employee\Model\Bank; | |
use Employee\Model\Employee; | |
use Zend\Hydrator\Exception\BadMethodCallException; | |
use Zend\Hydrator\HydratorInterface; | |
class EmployeeHydrator implements HydratorInterface | |
{ | |
/** | |
* Hydrate $object with the provided $data. | |
* | |
* @param array $data | |
* @param Employee $object | |
* | |
* @return Employee | |
*/ | |
public function hydrate(array $data, $object) | |
{ | |
if (!$object instanceof Employee) { | |
throw new \BadMethodCallException(sprintf( | |
'%s expects the provided $object:'.get_class($object).' to be a PHP Employee object)', | |
__METHOD__ | |
)); | |
} | |
// get a clean employee object and get the data from the db result (no prefix here!) | |
$employee = new Employee(); | |
$employee->exchangeArray($data); | |
// this is an example when you have a foreign key relation to the employee table itself (see the prefix) | |
$manager = new Employee(); | |
$manager->exchangeArray($data, 'manager.'); | |
$employee->setManager($manager); | |
$bank = new Bank(); | |
$bank->exchangeArray($data, 'bank.'); | |
$employee->setBank($bank); | |
return $employee; | |
} | |
/** | |
* @param Employee $object | |
* | |
* @return mixed | |
*/ | |
public function extract($object) | |
{ | |
if (!is_callable([$object, 'getArrayCopy'])) { | |
throw new BadMethodCallException( | |
sprintf('%s expects the provided object to implement getArrayCopy()', __METHOD__) | |
); | |
} | |
return $object->getArrayCopy(); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Model; | |
use Zend\Db\ResultSet\Exception\InvalidArgumentException; | |
use Zend\Db\Sql\Expression; | |
use Zend\Db\Sql\Select; | |
use Zend\Db\TableGateway\TableGatewayInterface; | |
class EmployeeTable | |
{ | |
/** | |
* @var TableGatewayInterface | |
*/ | |
private $tableGateway; | |
/** | |
* EmployeeTable constructor. | |
* | |
* @param TableGatewayInterface $tableGateway | |
*/ | |
public function __construct(TableGatewayInterface $tableGateway) | |
{ | |
$this->tableGateway = $tableGateway; | |
} | |
/** | |
* @param int $id | |
* | |
* @return Employee | |
*/ | |
public function getEmployeeById($id) | |
{ | |
/** @var Select $select */ | |
$select = $this->tableGateway->getSql()->select() | |
// example if you have a foreign key to the employee table itself | |
->join(['m' => 'employee'], 'employee.manager_id = m.id', | |
[ | |
'manager.id' => 'id', | |
'manager.forename' => 'forename', | |
'manager.surname' => 'surname', | |
], | |
Select::JOIN_LEFT) | |
->join(['b' => 'bank'], 'employee.bank_id = b.id', | |
[ | |
'bank.id' => 'id', | |
'bank.name' => 'name', | |
'bank.bic' => 'bic', | |
], | |
Select::JOIN_LEFT) | |
->where(['employee.id' => (int)$id]); | |
$result = $this->tableGateway->selectWith($select); | |
$row = $result->current(); // here you already have the employee object from your hydration | |
if (!$row) { | |
throw new InvalidArgumentException(sprintf( | |
'Employee with identifier "%s" not found.', | |
$id | |
)); | |
} | |
return $row; | |
} | |
// other database query methods | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Factory; | |
use Employee\Model\Employee; | |
use Employee\Model\EmployeeTable; | |
use Employee\Model\Hydrator\EmployeeHydrator; | |
use Interop\Container\ContainerInterface; | |
use Interop\Container\Exception\ContainerException; | |
use Zend\Db\Adapter\AdapterInterface; | |
use Zend\Db\ResultSet\HydratingResultSet; | |
use Zend\Db\TableGateway\TableGateway; | |
use Zend\ServiceManager\Exception\ServiceNotCreatedException; | |
use Zend\ServiceManager\Exception\ServiceNotFoundException; | |
use Zend\ServiceManager\Factory\FactoryInterface; | |
class EmployeeTableFactory implements FactoryInterface | |
{ | |
/** | |
* Create an object | |
* | |
* @param ContainerInterface $container | |
* @param string $requestedName | |
* @param null|array $options | |
* @return object | |
* @throws ServiceNotFoundException if unable to resolve the service. | |
* @throws ServiceNotCreatedException if an exception is raised when | |
* creating a service. | |
* @throws ContainerException if any other error occurs | |
*/ | |
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) | |
{ | |
$dbAdapter = $container->get(AdapterInterface::class); | |
$resultSetPrototype = new HydratingResultSet(); | |
$resultSetPrototype->setHydrator(new EmployeeHydrator()); // most interesting part | |
$resultSetPrototype->setObjectPrototype(new Employee()); | |
$tableGateway = new TableGateway('employee', $dbAdapter, null, $resultSetPrototype); | |
return new EmployeeTable($tableGateway); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Controller; | |
use Acl\Model\Role; | |
use Employee\Form\EmployeeForm; | |
use Employee\Model\Bank; | |
use Employee\Model\Employee; | |
use Employee\Model\EmployeeTable; | |
use Employee\Model\InsuranceCompany; | |
use Zend\Authentication\AuthenticationService; | |
use Zend\Form\FormInterface; | |
use Zend\Http\Request; | |
use Zend\Mvc\Controller\AbstractActionController; | |
class IndexController extends AbstractActionController | |
{ | |
/** | |
* @var AuthenticationService | |
*/ | |
private $auth; | |
/** | |
* @var EmployeeTable | |
*/ | |
private $employeeTable; | |
/** | |
* @var EmployeeForm | |
*/ | |
private $form; | |
/** | |
* IndexController constructor. | |
* | |
* @param AuthenticationService $auth | |
* @param EmployeeTable $employeeTable | |
* @param EmployeeForm $form | |
*/ | |
public function __construct(AuthenticationService $auth, EmployeeTable $employeeTable, EmployeeForm $form) | |
{ | |
$this->employeeTable = $employeeTable; | |
$this->auth = $auth; | |
$this->form = $form; | |
} | |
public function userAction() | |
{ | |
// get current employee object | |
$id = (int) $this->auth->getIdentity()['id']; | |
/** @var Employee $employeeObject */ | |
$employeeObject = $this->employeeTable->getEmployeeById($id); | |
$this->form->bind($employeeObject); | |
/** @var Request $request */ | |
$request = $this->getRequest(); | |
$viewData = ['id' => $id, 'form' => $this->form, 'name' => $employeeObject->forename . " " . $employeeObject->surname]; | |
if (!$request->isPost()) { | |
return $viewData; | |
} | |
$this->form->setData($request->getPost()); | |
if (!$this->form->isValid()) { | |
return $viewData; | |
} | |
$this->employeeTable->saveEmployee($employeeObject); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Employee\Factory; | |
use Employee\Controller\IndexController; | |
use Employee\Form\EmployeeForm; | |
use Employee\Model\EmployeeTable; | |
use Interop\Container\ContainerInterface; | |
use Interop\Container\Exception\ContainerException; | |
use Zend\Authentication\AuthenticationService; | |
use Zend\ServiceManager\Exception\ServiceNotCreatedException; | |
use Zend\ServiceManager\Exception\ServiceNotFoundException; | |
use Zend\ServiceManager\Factory\FactoryInterface; | |
class IndexControllerFactory implements FactoryInterface | |
{ | |
/** | |
* Create an object | |
* | |
* @param ContainerInterface $container | |
* @param string $requestedName | |
* @param null|array $options | |
* | |
* @return object | |
* @throws ServiceNotFoundException if unable to resolve the service. | |
* @throws ServiceNotCreatedException if an exception is raised when | |
* creating a service. | |
* @throws ContainerException if any other error occurs | |
*/ | |
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) | |
{ | |
$formManager = $container->get('FormElementManager'); | |
return new IndexController($container->get(AuthenticationService::class), $container->get(EmployeeTable::class), $formManager->get(EmployeeForm::class)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment