How to fetch Objects from Model/Service layer - service

In an app we are developing, we have Services, Mappers and Entities. We are not using an ORM. In the app, we have Group, GroupMember & Member entities. The GroupMember entity has the groupId, memberId & memberAccess properties. The memberAccess fields tells us the access level of the Member within the Group. Now we need to fetch
all the Groups where a member has specific level of access. (by providing a memberId)
all the Groups where a member is a member along with his access. (by providing a memberId)
all the Members within a group with their access. (by providing a groupId)
any ideas which service(s) should be used for each of the following. and how they will interact to fetch the specific data.

1) MembershipMapper: implement your queries here returning arrays of entities (have a look at ZfcBase\Mapper\AbstractDbMapper as base class)
function getGroupsForMember($member, $access = null)
function getMembersForGroup($group, $access = null)
2) MembershipServive: in Module.php under getServiceConfig you add this service to the service manager. Use a closure to create a new MembershipService and inject a MembershipMapper into it. The groups/members functions will probably just be proxies to the mapper.
function setMembershipMapper($membershipMapper)
function getMembershipMapper()
function getGroupsForMember($member, $access = null)
function getMembersForGroup($group, $access = null)
3) Controller: inject your MembershipService into your controller (you can do this in the same way you create your services with a closure). Then call the methods on the service.
Example for creating a service/mapper/controller in the service/controller manager (goes in Module.php)
public function getServiceConfig()
{
return array(
'factories' => array(
'MembershipService' => function (Zend\ServiceManager\ServiceManager $sm) {
$service = new YourNS\Service\Membership();
$service->setMembershipMapper($sm->get('MembershipMapper'));
return $service;
},
'MembershipMapper' => function ($sm) {
$mapper = new \YourNS\Mapper\Membership();
return $mapper;
},
);
}
public function getControllerConfig()
{
return array(
'factories' => array(
'YourNS\Controller\Something' => function (Zend\Mvc\Controller\ControllerManager $cm) {
$controller = new YourNS\Controller\Something();
$controller->setMembershipService($cm->getServiceLocator()->get('MembershipService'));
return $controller;
},
}

Related

ZF2 - text element, array name, validation

I've been reading a bit about form collections/fieldsets in ZF2 as I want to add some array fields to my form.
However as I understand - fieldsets/collections are build with domain entities in mind. In my situation this solution doesn't seem to be anywhere close to good idea.
My form is not related to any entity, it is just meant to pass params to pdf generator. These params are serialized and save in just one column in db.
Form itself is quite big and has a lot of fields like:
Question:
a) smth
b) smth
c) smth
d) other
a) other 1
b) other 2
...
x) other 22
So, I have multiple elements for extra input from user which have to be added dynamically.
Building a separated class for a fieldset containing ... 1 extra field each would result in atleast 20 extra classes per one form.
I thought I can make it simply by:
$element = new Element\Text('citiesOther[]');
$element->setAttributes(array(
'id' => 'citiesOther',
'placeholder' => 'Other city',
));
$this->add($element);
//view:
$this->formElement( $form->get( 'citiesOther[]' )
And then in the frontend there was a button "add another" which was just cloning whole input. It worked quite well, I could receive params from the post.
The thing is... I am unable to filter/validate these fields. If I pass a name "citiesOther[]" to inputFilter, it's not even validating one of these fields, not even mentioning whole array.
Collections in Zend Framework are a bit tricky to understand. Basically you need a basic fieldset for a collection. This fieldset implements the InputFilterProviderInterface for filtering and validation. With this in mind, you have to define all your repeatable input fields and the validation for this fields in a collection. It is not a good idea not validating this data. There are a few scenarios for injecting really bad data in a pdf. So please validate all data, which is coming from an form. Always!
Here 's a quick example how collection with Zend Framework are working.
namespace Application\Form;
use Zend\Form\Fieldset;
use Zend\Form\Element\Text;
use Zend\InputFilter\InputFilterProviderInterface;
class YourCollection extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'simple',
'type' => Text::class,
'attributes' => [
...
],
'options' => [
...
]
]);
}
public function getInputFilterSpecification()
{
return [
'simple' => [
'required' => true,
'filters' => [
...
],
'validators' => [
...
],
],
];
}
}
This is the basic fieldset class, which acts as collection of all input fields you want to repeat in your form. This basic fieldset filters and validates itself by implementing the InputFilterProviderInterface. By using the implemented method, you can place filters and validators for your inputs
If you have dependent input fields, you have to define them here in this fieldset.
For using it right, you need an entity, which is bound to the basic fieldset later in this post. Next, we create the entity for this fieldset.
namespace Application\Entity;
class YourCollectionEntity
{
protected $simple;
public function getSimple()
{
return $this->simple;
}
public function setSimple($simple)
{
$this->simple = $simple;
return $this;
}
}
This is a very simple entity. This will act as your data holder and will be bound to the collection later. Next we need a fieldset, that contains the collection and a hidden field for the collection count. Sounds a bit complecated using another fieldset. But in my eyes this is the best way.
namespace Application\Form;
class YourCollectionFieldset extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'collectionCounter',
'type' => Hidden::class,
'attributes' => [
...
],
]);
// add the collection fieldset here
$this->add([
'name' => 'yourCollection',
'type' => Collection::class,
'options' => [
'count' => 1, // initial count
'should_create_template' => true,
'template_placeholder' => '__index__',
'allow_add' => true,
'allow_remove' => true,
'target_element' => [
'type' => YourCollection::class,
],
],
]);
}
public function getInputFilterSpecification()
{
return [
'collectionCounter' => [
'required' => true,
'filters' => [
[
'name' => ToInt::class,
],
],
],
];
}
}
This fieldset implements your collection and a hidden field, which acts as a counter for your collection. You need both when you want to handle collections. A case could be editing collection contents. Then you have to set the count of the collection.
As you can imagine, you need an entity class for this fieldset, too. So let 's write one.
namespace YourCollectionFieldsetEntity
{
protected $collectionCounter;
protected $yourCollection;
public function getCollectionCounter()
{
return $this->collectionCounter;
}
public function setCollectionCounter($collectionCounter)
{
$this->collectionCounter = $collectionCounter;
return $this;
}
public function getYourCollection()
{
return $this->yourCollection;
}
public function setYourCollection(YourCollectionEntity $yourCollection)
{
$this->yourCollection = $yourCollection;
return $this;
}
}
This entity contains the setYourCollection method, which takes an YourCollectionEntity instance as parameter. We will see later, how we will do that little monster.
Let 's wrap it up in a factory.
namespace Application\Form\Service;
YourCollectionFactory
{
public function __invoke(ContainerInterface $container)
{
$entity = new YourCollectionFieldsetEntity();
$hydrator = (new ClassMethods(false))
->addStrategy('yourCollection', new YourCollectionStrategy());
$fieldset = (new YourCollectionFieldset())
->setHydrator($hydrator)
->setObject($entity);
return $fieldset;
}
}
This factory adds a hydrator strategy to the hydrator. This will hydrate the entity bound to the repeatable fieldset.
How to use this in a form?
The above shown classes are for the needed collection only. The collection itself is not in a form yet. Let 's imagine we have a simple form, which implements this collection.
namespace Application\Form;
class YourForm extends Form
{
public function __construct($name = null, array $options = [])
{
parent::__construct($name, $options);
// add a few more input fields here
// add your collection fieldset
$this->add([
'name' => 'fancy_collection',
'type' => YourCollectionFieldset::class,
]);
// add other simple input fields here
}
}
Well, this is a simple form class, which uses the collection shown above. As you have written, you need a validator for validating all the data this form contains. No form validator without an entity. So first, we need an entity class for this form.
namespace Application\Entity;
class YourFormEntity
{
// possible other form members
...
protected $fancyCollection;
public function getFancyCollection()
{
return $this->fancyCollection;
}
public function setFancyCollection(YourCollectionFieldsetEntity $fancyCollection)
{
$this->fancyCollection = $fancyCollection;
return $this;
}
// other getters and setters for possible other form fields
}
... and finally the validator class for your form.
namespace Application\InputFilter;
class YourFormInputFilter extends InputFilter
{
// add other definitions for other form fields here
...
}
}
We don 't need to redefine filters and validators here for your collection. Remember, the collection itself implements the InputFilterProviderInterface which is executed automtically, when the validator for the form, which contains the collection, is executed.
Let 's wrap it up in a factory
namespace Application\Form\Service;
class YourFormFactory
{
public function __invoke(ContainerInterface $container)
{
$container = $container->getServiceLocator();
$entity = new YourFormEntity();
$filter = new YourFormInputFilter();
$hydrator = (new ClassMethods(false))
->addStrategy('fancy_collection', new FancyCollectionStrategy());
$form = (new YourForm())
->setHydrator($hydrator)
->setObject($entity)
->setInputFilter($filter);
return $form;
}
}
That 's all. This is your form containing your collection.
Hydrator strategies are your friend
Above two hydrator strategies are added to the hydrators you need for your form. Adding hydrator strategies to a hydrator works like a charme, if you have complex post data, which is ment to be pressed in enities.
namespace Application\Hydrator\Strategy;
class YourCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
$entities = [];
if (is_array($value)) {
foreach ($value as $key => $data) {
$entities[] = (new ClassMethods())->hydrate($data, new YourCollectionEntity());
}
}
return $entities;
}
}
This hydrator strategy will hydrate the entity four the repeated collection fieldset. The next strategy will hydrate the whole collection data into your form entity.
namespace Application\Hydrator\Strategy;
class FancyCollectionStrategy extends DefaultStrategy
{
public function hydrate($value)
{
return (new ClassMethods())
->addStrategy('yourCollection', new YourCollectionFieldsetEntity())
->hydrate($value);
}
}
This one will hydrate the collection count and the repeated fieldset. That 's all for the hydration of your data.
How does that look in a controller?
Well, that 's simple. Now, as we have all the classes we need for a complex form with collection, wie can go on with the controller.
namespace Application\Controller;
class YourController extends AbstractActionController
{
protected $form;
public function __consturct(Form $form)
{
$this->form = $form;
}
public function indexAction()
{
$request = $this->getRequest();
if ($request->isPost()) {
$this->form->setData($oRequest->getPost());
if ($this->form->isValid()) {
// get all data as entity
$data = $this->form->getData();
// iterate through all collection data
foreach ($data->getFancyCollection()->getYourCollection() as $collection) {
// get every simple field from every collection
echo $collection->getSimple();
}
}
}
}
}
Sure, this is way more complex than just rerieving the raw post data. But as a mentioned before, you should not use the raw data because of security reasons. Always validate and filter data, which was given by a user over a form. Or just to keep it simple: do not trust the user!

Laravel one to many relationship save method empty attributes

I am trying to save a relationship with the Laravel save method:
public function storeContact(Request $request)
{
$user = User::firstOrNew(['email' => $request->input('email')]);
$user->save();
$message = new App\Message([
'message' => $request->input('remarks')
]);
$user->message()->save($message);
}
var_dump($request->all) confirms both fields are available in the request.
All relations work. hasOne, belongsTo are configured in the models. The relation is saved like expected (but message field is empty)
When I var_dump($message), there are no attributes in the collection.
I already tried fillable and guarded on the models without any effect. These should not be necessary for the save method though because this uses a full Eloquent model instance.
What am I missing here??
I think I found the culprit.
When I use a __construct on a model it fails. When I instantiate a model with a __construct no variables are passes as attributes. Even if the __construct is empty.
Test with __construct method
class Message extends Model
{
protected $connection = 'system';
public function __construct()
{
}
// ...
}
$message = new App\Message(['remarks' => 'Test remarks']);
var_dump(message) // does NOT contain attributes!
Test2 without __construct method
class Message extends Model
{
protected $connection = 'system';
// ...
}
$message = new App\Message(['remarks' => 'Test remarks']);
var_dump(message) // does contain attributes!
This looks like a bug in Laravel to me.

ZF2 instantiating classes from factories using MutableCreationOptionsTrait: creates the same instance of class no matter what

In ZF2, I have a factory like this
class SomeServiceFactory implements FactoryInterface, MutableCreationOptionsInterface
{
use MutableCreationOptionsTrait;
public function createService(ServiceLocatorInterface $serviceLocator)
{
$serviceManager = $serviceLocator->getServiceLocator();
$formElementManager = $serviceManager->get('FormElementManager');
if ($this->creationOptions == 'value1') {
return new SomeService(
$formElementManager->get('Path\To\Form1'),
$serviceManager->get('Path\To\Mapper1'),
new Object1()
);
} elseif ($this->creationOptions == 'value2') {
return new SomeService(
$formElementManager->get('Path\To\Form2'),
$serviceManager->get('Path\to\Mapper2'),
new Object2()
);
}
}
}
In the controller factory, I get several instances of SomeService based on the option value attached at the object creation, like
$service1 = $viewHelperManager->get('Path\To\SomeService', ['valueType' => 'value1']);
$service2 = $viewHelperManager->get('Path\To\SomeService', ['valueType' => 'value2']);
(these services are view helpers with their dependencies).
The problem is that $service2 is the exact same object as $service1 whereas it should have different dependencies. I tried to study the thing a bit, and it seems that the $creationOptions are not updated when assigning $service2 despite the valueType is completely different.
What is wrong?
Accidentally bumped into the answer in the comments (#AlexP if you can hear me, thanks buddy!) to the following question:
https://stackoverflow.com/a/28205176/4685379
By default, ZF2 shares the services. If you want to create a new service each time you call the factory, you need to specify the shared directive in the module.confing.php under the appropriate item like this:
'view_helpers' => [
'factories' => [
// some factories including the name of the one that
// you don't want to create new instances each time it's called
],
'shared' => [
'Alias\Of\That\Factory' => false,
],
],
http://framework.zend.com/manual/current/en/modules/zend.service-manager.quick-start.html#sample-configuration

Symfony3 - how to compare properties from 2 different entities

I want to create a view where an event is displayed only if it is owned by the currently logged in user.
So I want to compare LoginID from the Events entity to the current user ID.
I tried with the following function:
public function showAction(Events $event, $id)
{
$em = $this->getDoctrine()->getManager();
$user=$this->getUser()->getLoginid();
$guests = $em->getRepository('VendorMyBundle:Guests')->findByEventid($id);
$events = $em->getRepository('VendorMyBundle:Events')->findByEventid($id);
// condition to display only events owned by the current user
if ($events->getLoginid()==$user){
$session = new Session();
$session->set('eventid', $id);
$deleteForm = $this->createDeleteForm($event);
return $this->render('events/show.html.twig', array(
'event' => $event,
'delete_form' => $deleteForm->createView(),
'guests' => $guests,
));
}
$this->addFlash('error', 'The event does not exist or you do not have permission to view it.');
return $this->redirectToRoute('home_page');
}
LoginID in Events entity is a Many-to-One relation property towards the Logins entity which is my user provider entity.
When I try to view with that method in my controller I get this: Error: Call to a member function getLoginid() on a non-object on the line with the IF statement.
Additional question would be, how do I compare 2 property values from 2 different entities?
Since the problem is that you are returning an array you have two choices, update the findByEventid() to return a single result
return $query->getSingleResult();
or
if ($events[0]->getLoginid()==$user){
..
}
Hope this help you.

How to write a custom row class (extends Zend_Db_Table_Row) for a Db_Table class in Zend Framework

I have separate db_table classes for books, book_sections and users (system end users). in book table has columns for book_title, section_id(book_section) , data_entered_user_id(who entered book info).
go to this url to see the image(I'm not allow to post images bacause I'm new to stackoverflow)
img685.imageshack.us/img685/9978/70932283.png
in the backend of the system I added a form to edit existing book (get book id from GET param and fill the relevant data to the form). form has elements for book_title, book_section and data_entered_user.
to get exciting book data to "edit book form" I join book_section and user tables with book table to get book_section_name and username(of data_entered_user: read only- display on side bar)
go to this url to see the image(I'm not allow to post images bacause I'm new to stackoverflow)
img155.imageshack.us/img155/2947/66239915.jpg
In class App_Model_Book extends Zend_Db_Table_Abstract
public function getBookData($id){
$select = $this->select();
$select->setIntegrityCheck(false);
$select->from('book', array('id','section_id','data_entered_user_id',...));
$select->joinInner('section','book.section_id = section.id',array('section_name' =>'section.name' ));
$select->joinInner(array('date_entered_user' => 'user'),'book.date_entered_user_id = date_entered_user.id',array('date_entered_user_name' =>'date_entered_user.user_name' ));
$select->where("book.id = ?",$id);
return $this->fetchRow($select);
}
public function updateBookData($id,$title,$section_id,...)
{
$existingRow = $this->fetchRow($this->select()->where('id=?',$id));
$existingRow->title = $title;
$existingRow->section_id = $section_id;
//....
$existingRow->save();
}
In Admin_BookController -> editAction()
$editForm = new Admin_Form_EditBook();
$id = $this->_getParam('id', false);
$bookTable = new App_Model_Book();
$book_data = $bookTable ->getBookData($id);
//set values on form to print on form when edit book
$editForm->book_title->setValue($book_data->title);
$editForm->book_section->setValue($book_data->section_name);
//........
//If form data valid
if($this->getRequest()->isPost() && $editForm->isValid($_POST)){
$bookTable = new App_Model_Book();
$bookTable ->updateBookData(
$id,
//get data from submitted form
$editForm->getValue('title'),
//....
);
when editing an exsiting book
get data from getBookData() method on App_Model_Book class
If form data is valid after submiting edited data, save data with updateBookData() method on App_Model_Book class
but I saw that if I created a custom
Db_Table_Row(extends Zend_Db_Table_Row)
class for book table with
book_section_name and
data_entered_user_name I can use it
for get existing book data and save it
after editing book data without
creating new Book(db_table) class and without calling updateBookData() to save updated data.But I don't know which code I should write on custom
Db_Table_Row(extends Zend_Db_Table_Row)
class.
I think you can understand my problem, in simple
how to write a custom db_table_row class to create a custom row with data
form 2 joined tables for a perticular db_table class ?
I'm new to zend framewok and to stackoverflow. forgive me if you confused with my first question.
Thanks again.
1) at your db_table class create field which contain row class, for example:
protected $_rowClass = 'App_Model_Db_Books_Row';
and add reference map for parent tables:
protected $_referenceMap = array(
'Section' => array(
'columns' => 'sectionId',
'refTableClass' => 'App_Model_Db_Sections',
'refColumns' => 'id'
),
'User' => array(
'columns' => 'userId',
'refTableClass' => 'App_Model_Db_Users',
'refColumns' => 'id'
)
);
2) at row class you must define variables for parent tables:
protected $section;
protected $user;
In such cases i create method called "load":
public function load()
{
$this->section = $this->findParentRow('App_Model_Db_Sections');
$this->user = $this->findParentRow('App_Model_Db_Users');
}
And in constructor i call this method:
public function __construct(array $config = array())
{
parent::__construct($config);
$this->load();
}
As a result after:
$book_data = $bookTable ->getBookData($id);
you can access data from reference table, for example:
$book_data->section->name = "Book Section";
$book_data->section->save();