I'm working with a Symfony 2.1 application and I have a lot of parameters being sent via a POST request and I'm looking for a smarter way to take each request parameter and populate the my entity class. I'm looking to avoid writing $entity->setMyParam($my_param) expressions for n request parameters. For example, here is a snippet of my entity:
namespace Brea\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Brea\ApiBundle\Entity\Distributions
*
* #ORM\Table(name="distributions")
* #ORM\Entity
*/
class Distributions
{
/**
* #var string $recordType
*
* #ORM\Column(name="record_type", type="string", nullable=false)
* #Assert\NotBlank()
* #Assert\Choice(choices = {"a", "b", "c", "d", "e"}, message = "Choose a valid record type")
*/
private $recordType;
/**
* Set recordType
*
* #param string $recordType
*/
public function setRecordType($recordType)
{
$this->recordType = $recordType;
}
/**
* Get recordType
*
* #return string
*/
public function getRecordType()
{
return $this->recordType;
}
}
My controller attempts to take each request, camelcase the parameters and set the value of the request param to the entity:
public function createRecordAction(Request $request, $id)
{
$distribution = new Distributions();
$params = $request->request;
foreach ($request->request->all() as $param=>$value)
{
if ($param == "_method")
continue;
$function = "set".str_replace(' ','',ucwords(preg_replace('/[^A-Z^a-z^0-9]+/',' ',$param)));
$distribution->$function($value);
}
}
It works, but my misgiving about this approach is that I would need to run this code in every controller that does a similar thing. I can refactor it into a parent class as a method to avoid duplicating code, but I'm curious if this is a good practice. I looked for something in the Symfony framework that does this already but all I could find were examples of binding the request to a form.
First of all: Warning!!
As I commented earlier, I would be very careful about using the code provided in your original post since you said it's data from a POST request, which means a client could inject any kind of data in it and call functions you might not have wanted on your object (or just cause a failure in your script by sending you non-existant function names).
I would actually read the conclusion first..! :) Then come back to Alt. 1 & 2.
Alternative 1:
With that being said, an alternative solution to your problem would be to give objects the responsability to fetch their own data. With granulated enough objects, you should not end up with bloated code, and you can define in each class which parameters to look for and which functions to call (and localize changes when you make a change to a class):
class BookInformation{
private $publisher;
private $name;
private $price;
public static createFromRequest($req){
$publisher = Publisher::createFromRequest($req);
$book = new BookInformation($publisher, $req['book_name'], $req['book_price']);
$book->setABC($req['abc']);
//...
return $book;
}
public __construct($publisher, $name, $price){
//...
}
}
class Publisher{
private $name;
private $address;
public static createFromRequest($req){
return new Publisher($req['publisher_name'], $req['publisher_address']);
}
public __construct($name, $address){
//...
}
}
Like I said before, one big advantage with this method is that if you need to add new attributes to any of those classes, you don't have to edit the controllers at all and can just edit your "initialization from request method". Future changes will be localized to the modified class.
Of course, don't forget to validate any data sent from a user request (but that's just common sense).
Alternative 2:
Note that the first alternative is very similar to the Factory pattern (based on GoF's Abstract Factory), and that you could also implement a solution using that pattern:
class BookFactory{
public createBookInformation($req){
$publisher = $this->createPublisher($req);
$book = new BookInformation($publisher, $req['book_name'], $req['book_price']);
$book->setABC($req['abc']);
//...
return $book;
}
public createPublisher($req){
return new Publisher($req['publisher_name'], $req['publisher_address']);
}
//createAnythingRelatedToBooks($req)...
}
That way, you have all the initializing procedures in a very cohesive class which only responsability is to initiliaze a certain family of objects based on a request object (and that is a VERY good thing). However, if you add attributes to one of those classes, you have to edit the appropriate Factory method too.
Conclusion
Please note that these two alternatives are actually not really alternatives... They can be used along with your initial code (especially the Factory one). They really only solve your last problem (the "where to put the code" problem).
However, even if you do sanitize the POST request and only call functions that are annoted (as stated earlier), I wouldn't really suggest it because I have the feeling that more complex business rules would ruin the design pretty fast (but maybe you've got it all covered already (?)). That is, I don't think you can easily plug in business rules in the initializing process since it's all automatic (it can't do any validation of the values since it could be any kind of value) and I feel like you'd end up "undo-ing" stuff after initialization (which I personally hate.. Lots of room for errors)!
For example, take the same two classes in Alternative 1 (BookInformation and Publisher).
Let's say a Book can only have a Publisher if that Publisher is already registered in a database and that their address has been confirmed (new publishers need to be created using another interface and then have their address confirmed before they can be linked to a book).
Else, regardless of the request data, publisher should be set to XYZ. I have the feeling (I might be wrong) that to support those kind of rules, you'd have to actually construct the object (automatically) and then destroy/reassign the publisher attribute if it does not match certain rules. Now, if you have a pool of those Publisher objects in memory, you also need to remember to delete the wrongly created Publisher in that pool. And that's just one rule!
One thing you could do with your code to "fix" that issue would be to have a validation method for each setter (validXYZ()), but that's starting to look like a design that would fall apart pretty quickly if validations are dependent on other objects/data...
I don't really have anything else to discourage you from using that code, but if you do, please keep us updated on how it works out after a year or two (once there has been some maintenance/new features added, etc...).
I looked for something in the Symfony framework that does this already but all I could find were examples of binding the request to a form.
I would use Forms for this. Even if the HTTP request is not performed from an HTMl form, you can just bind the Request to a form instance: it will take care of all the data injection and validation.
And plus, in case you'll ever need HTML forms, you'll have them ready ^^.
Related
Looking more into OOP and classes, I have an extremely large user class which is becoming increasingly large to manage. The total code is in excess of 950 lines and completely dismisses the Single Responsibility principle.
A few ways i thought to make this smaller is to create completely seperate classes and transfer the data in on the construction of the new class. For example:
index.php
$user = new user('username');
$userOptions = new userOptions($user);
or
$user = new user;
$userOptions = new userOptions;
$userOptions->setData(
$user->getData([
'username'=>'genericuser'
]),
);
However this seems un-natural and isn't as nice as i would expect properly formatted code to be.
Another way i thought of is to extend the base class into another one. For example:
index.php
$userOptions = new userOptions('username');
classes/useroptions.php
class userOptions extends User {
//a small section of what would be a big user class
}
However this also violates some PHP practices, with extends normally meaning a special case of which user options doesn't appear to be.
My final way of organising the document uses in the default class to seperate the large file down, for example:
classes/user.php
class User {
/**
* CREATION SECTION
*/
public function create() {
//create user
}
/**
* UPDATE SECTION
*/
public function change($whattochange) {
//what to change
}
}
However this once again seems to violate the single responsibility principle, with a lot of options being in one single class.
What is common practice to separate down a class, and how should it be done?
The current User class has the following methods inside of it:
* - $variables
* - __construct gets a new user.
* - stripeCustomer() gets the customer information if it exists
* - create($fields|array) method to create a new user
* - login($username|string, $password|string, $remember|bool) logs in a user
* - find ($param|string, $method|string) finds a user WHERE param=method
* - data() returns user data
* - isLoggedIn() returns whether a user is logged in
* - isAdmin() returns if a user is an admin
* - logout() logs out current user
* - displayName() returns first name or username
* - isVerified() checks if a user is verified
* - inCompany() checks if a user is in a company
* - inVerifiedCompany() checks if a user id in a verified company
* - verifyUser($apicode|string, $verification|string, $resend|bool) verifies a users email
* - error() returns any errors that may occur
* - getProfilePicture($size|int) gets a users profile picture from Gravatar with size of $size in pixels
* - passwordCheck($password|string) checks if two passwords match
*
* // 2FA section
* - has2FA() checks if the user has 2FA enabled
* - TOTP($type|string, $secret|string, $code|string, $backupcodes|array) everything to do with 2FA
* - countBackups() counts the amount of backup codes remaining for a user with 2FA
* - update($statement|string, $params|array, $apicode|string) updates a user
*
* // lockdown system
* - getAttempts() gets amount of attempts to break into a users account
* - isLocked() gets whether the user account is locked
* - addAttempt() adds an attempt to a users account
* - reinstateUser() unlocks a users account
* - shouldShowCaptcha() checks whether a captcha is needed
*
* // codes
* - sendRequest($code|int) sends a request to a users email with a specific code
* - verifyCode($code|string, $type|int) checks a user inputted code to one in the DB
*
* - deleteUser() deletes the specific user
* - makeAdmin() makes the user an admin
* - removeAdmin() removes the user as an admin
* - modify($changes|array, $apicode|string) modifies a user | no idea how this is different to update
I also understand that the database section of the class should be in a seperate mapper class, which would use the same style structure as my initial attempt, this will be changed soon.
Thanks in advance for all help.
For reference, i've had a look on google and found some people asking similar questions, however none seem to answer the question to a great degree.
How to break up a large class
there is a way which is called trait you can make any number of traits and include then in a class.. make seperate files of traits it will make your code easy to read..
What a trait actually is its like a class with lot of methods..
how to use it
the syntax will be something like this..
trait UserOptions{
public function create()
{
// logic goes here
}
public function destroy(){
// logic
}
}
trait UserAdmin{
public function isAdmin(){
return true;
}
}
class User{
// this is how to include traits
use UserOptions , UserAdmin ;
}
now all the methods of all the traits are included in the User class .
That is how we can break down so much of the code
Well, I am not really an expert myself. But I guess the answer to the question has two sides. One is not really technical. Let's start with it.
Refactoring is not a trivial task to do. Refactoring legacy code is twice so (I am not sure that your code is actually legacy one, but I guess the rule holds). Usually done by the people who are experts in their field. And even before they start there are a couple of steps to be done. Firstly, the current behaviour of the system must be documented. Either with some kind of end-to-end (acceptance) tests or by really thorough technical documentation (done by business analytics). Better with both. And only then you can start the process of rewriting. Without this, you simply cannot be sure the system will work as before. It the best case scenario, you will end up with explicit bugs (the ones you can clearly see right away). But you also can end up with some unexpected implicit behaviour, that you might notice only after some time.
Regarding technical side. Now you have so-called God object. It does at least persistence, authentication and authorization. So as you stated SRP (Single responsibility principle) is completely dismissed. So firstly you have to extract entity (business object):
final class User
{
private $id;
private $name;
// ...
public function __construct($id, $name)
{
$this->id = $id;
$this->name = $name;
}
public function id()
{
return $this->id;
}
public function name()
{
return $this->name;
}
}
Then extract repository to persist this entity:
final class UserRepository
{
/**
* \PDO or whatever connection you use.
*/
private $db;
public function __construct(\PDO $db)
{
$this->db = $db;
}
public function findByPk($id): User {}
public function create(User $user) {}
public function update(User $user) {}
public function delete(User $user) {}
}
Extract authentication and authorization into separate services. Even better, use existing packages (you can find many on GitHub).
But again all this is safe to do only if you have the way to ensure that your changes have not broken anything.
There is a book on the subject. To be honest, I have not read it myself yet, but I've listened to the podcast with the author and heard really good reviews. So you can check it out.
Instead of stitching rotten software you should think of a re-design with a migration process. The best is to create a new branch that can cope with old data structures but allows to introduce new (straight) ones. A clean cut is also better than many little migration steps. There's a risk in both: little step take long and each can cause user frustration. A cut can fail and you might need to roll back.
Let's assume I have a Booking entity and it has a state field which may be set to one of a few values - let's make it: NEW, ACCEPTED, and REJECTED
I am looking for the "right" way to implement this. So far I used an approach like this:
class Booking
{
const STATUS_NEW = 0;
const STATUS_ACCEPTED = 1;
const STATUS_REJECTED = 2;
protected $status = self::STATUS_ACTIVE;
}
And it works ok, but I am really curious of the "proper" way of doing, also I have a few problems with this approach:
It looks awfully lot like a business logic hidden in the entity class - if entity is supposed to be a POJO, then why would it care what the status may be? So I could put it in a manager class like:
class BookingManager
{
const STATUS_NEW = 0;
const STATUS_ACCEPTED = 1;
const STATUS_REJECTED = 2;
public function setBookingStatus(Booking $b, $status) { }
}
but it still does not help with the second issue:
It is hard to re-use that data in the view, let's take a twig for example - I would have to create a Twig Extension in order to convert a number into an actual name:
Status type: {{ booking.status }}
Status name: {{ booking.status|statusName }}{# I don't like this approach #}
Status name: {{ booking.getStatusName() }} {# This seems even worse #}
So I could add getStatusName method to the BookingManager but I believe it does not belong there. I could add the same method to the Booking class and it would work just fine, but then again - business logic AND now also presentation logic is hidden in the entity.
If some of my code depends on MyVendor\MyBundle\Entity\Booking::STATUS_NEW then could it become a problem when unit-testing the code? With static method calls the problem is obvious - I cannot mock the dependency. Is there any use case where depending on static constants may be a problem?
All I could think of would be moving all this logic to the service layer and create a service like BookingStatusManager (let's ignore the fact that it could be mistakenly took for an EntityManager subclass) - it could look like this:
class BookingStatusManager
{
const STATUS_NEW = 0;
const STATUS_ACCEPTED = 1;
const STATUS_REJECTED = 2;
public function getStatusName($code) { ... }
}
but now I need an extra class for each enum-like property for each entity and it's a pain, also it still does not seem right - I need to reference static values from this class whenever I want to deal with Booking's status.
Note that this is a general question, not one specific to presented Booking example - it could be for example BorderLength entity with lengthUnit field with possible values of MILES or KILOMETERS; also, status transitions are not restricted to those caused by users, it must be possible to perform these from the code.
What in your experience is "the right way" to solve this?
I'd propose you get rid of of the constants and just create a Many-To-One association on your Booking entity with a new BookingStatus entity. Why? Well for a number of reasons:
Does not require editing of code if you wish to add a new booking status. Not only is this easier for developers but also allows the possibility of statuses to be dynamically created.
You can easily store additional information about the status in the new BookingStatus entity, e.g name. This information can also be updated without altering code.
Allows external tools to understand different statuses. For example you might want to use a external reporting tool directly on the database. It won't know what some integers mean but it will be able to understand a Many-To-One association.
Answers to your numbered questions
1: Why wouldn't you have business logic in your business model/objects?
Coupling data and behaviour is, after all, one of the very purposes of object orientation? I believe you may have misunderstood the "POJO" concept (and I'm not talking about the J standing for Java ;)), whose purpose was to not let frameworks invade your model, thus limiting the model to a framework-specific context and making unit testing or re-use in any other context difficult.
What you're talking about sounds more like DTOs, which generally is not what your model should consist of.
2: Yes, Twig is not terribly good at manipulating numbers-that-symbolizes-meaning. You'll probably get all kinds of suggestions based on things like optimization of storage (number of bytes) or database traffic/query time, but for most projects I prefer prioritizing the human experience - i.e. don't optimize for computers unless you need it.
Thus, my personal preference (in most circumstances) is instantly dev-readable "enum" fields, but in a key-like notation instead of regular words. For example, "status.accepted" as opposed to 1 or "Accepted". Key-like notations lend themselves well to i18n, using twig's |trans filter, {% trans %} tag or something similar.
3: Static "enum" references within your model is rarely a problem while unit testing the model itself.
At some point your model needs to define its semantics anyway, through the use of the building blocks you have available. While being able to abstract away implementations (particularly of services) is useful, being able to abstract away meaning is rarely (never?) fruitful. Which reminds me of this story. Don't go there. :-D
If you're still concerned about it, put the constants in an interface that the model class implements; then your tests can reference only the interface.
Suggested solution
Model, alternative 1:
class Booking {
const STATUS_NEW = 'status.new';
const STATUS_ACCEPTED = 'status.accepted';
const STATUS_REJECTED = 'status.rejected';
protected $status = self::STATUS_NEW;
}
Model, alternative 2:
interface BookingInterface {
const STATUS_NEW = 'status.new';
const STATUS_ACCEPTED = 'status.accepted';
const STATUS_REJECTED = 'status.rejected';
// ...and possibly methods that you want to expose in the interface.
}
class Booking implements BookingInterface {
protected $status = self::STATUS_NEW;
}
Twig:
Status name: {{ ("booking."~booking.status)|trans({}, 'mybundle') }}
(Of course, the booking. prefix is optional and depends on the way you want to structure your i18n keys and files.)
Resources/translations/mybundle.en.yml:
booking.status.new: New
booking.status.accepted: Accepted
booking.status.rejected: Rejected
Constants-as-entities
On Tomdarkness' suggestion of turning these constants into their own model class, I want to stress that this should be a business/domain decision, and not a question of technical preference.
If you clearly foresee the use cases for dynamically adding statuses (supplied by the system's users), then by all means a new model/entity class is the right choice. But if the statuses are used for internal state in the application, which is coupled to the actual code you're writing (and thus won't change until the code changes too), then you're better off using constants.
Constants-as-entities makes working with them much harder ("hm, how do I get the primary key of 'accepted', again?"), isn't as easily internationalizable ("hm, do I store the possible locales as hard-coded properties on the BookingStatus entity or do I make another BookingStatusI18nStrings(id, locale, value) entity?"), plus the refactoring issue you brought up yourself. In short: don't overengineer - and good luck. ;-)
I use very simple approach. Example:
class Offer extends Entity implements CrudEntityInterface
{
const STATUS_CANCELED = -1;
const STATUS_IN_PROGRESS = 0;
const STATUS_FINISHED = 1;
const STATUS_CONFIRMED = 2;
protected static $statuses_names = [
self::STATUS_CANCELED => 'canceled',
self::STATUS_IN_PROGRESS => 'in_progress',
self::STATUS_FINISHED => 'finished',
self::STATUS_CONFIRMED => 'confirmed'
];
/**
* #var integer
*
* #ORM\Column(name="status", type="integer")
* #Assert\NotBlank
*/
protected $status = self::STATUS_IN_PROGRESS;
public static function getStatuses()
{
return self::$statuses_names;
}
/**
* Set status
*
* #param integer $status
* #return Offer
*/
protected function setStatus($status)
{
if(!array_key_exists($status, self::$statuses_names)){
throw new \InvalidArgumentException('Status doesn\'t exist');
}
$this->status = $status;
return $this;
}
/**
* Get status
*
* #return integer
*/
public function getStatus()
{
return $this->status;
}
public function getStatusName()
{
return self::$statuses_names[$this->status];
}
}
All display names are always translated, in order to keep separation from model
{{ ('offer.statuses.'~offer.statusName)|trans }}
While it's not the most elegant, you can get a little pragmatic, and just use string keys in a fairly efficient, safe, way:
<?php
class Booking
{
protected $statusMap = array(
0 => 'new',
1 => 'accepted',
2 => 'rejected'
);
/**
* A premature optimization, trading memory to reduce calls to
* array_flip/array_search in a more naive implementation
*
* #var array
*/
protected $statusMapReverse;
public function __construct(){
$this->statusMapReverse = array_flip($this->statusMap);
$this->setStatus('new');
}
/**
* #ORM\Column(type="integer")
*/
protected $status;
/**
* #param string $status Valid values are 'new', 'accepted', and 'rejected'
*
* #throws InvalidBookingStatusException
*/
public function setStatus($status){
if (! in_array($status, $this->statusMap)){
throw new InvalidBookingStatusException();
}
$this->status = $this->statusMapReverse[$status];
}
/**
* #return string
*/
public function getStatus(){
return $this->statusMap[$this->status];
}
/**
* #return integer
*/
public function getStatusCode(){
return $this->status;
}
/**
* #return array
*/
public function getStatusMap(){
return $this->statusMap;
}
}
I find I use this pattern pretty frequently when I need to model this kind of enumeration-like data. It has a few nice features:
1) Status is stored as in integer in the db.
2) Calling code doesn't ever need to care about those (integer) values, unless it wants to.
3) Calling code is protected against typos/invalid status since setStatus validates strings (can only be checked at runtime, but hey)
4) Booking::getStatusMap() makes it easy to generate select boxes, etc.
You could expand setStatus to accept either a string or integer (status-code), and still validate, if you wanted to be able to set status by code.
The disadvantages (compared to using constants), are mainly:
1) Querying by status becomes a bit of a hassle. Client code needs to getStatusMap() and find codes.
2) IDEs won't be quite as helpful in telling calling code what valid statuses are (though the phpdoc annotation on setStatus() can help, if you keep it up-to-date.
3) Invalid status throws an exception at runtime, while with constants you find out at compile-time.
--
Final note, I don't see the need for a BookingManager at all. Yes, entities are just plain objects, but that doesn't mean they can't contain logic to manage their own state.
I like handling those kind of issues in a generic way (a bit of an overkill if all you need is a status, but what project ever needed only one enum-like field?);
First, I create a "GenericOptionEntity", having the following fields:
ID
Tag
Label
Entity
Field
And a one to many relationship to associate the entity (or the base entity, in case of a generic "status") to the appropriate field.
The "entity" and "field" fields help identify the proper options for form types.
The GenericOptionRepository should implement a "getOptionByTag", so that you wouldn't have to use IDs to get the proper entity. Also, a generic method for getting appropriate options for the calling method \ field.
Lastly (depending on the case), a bunch of interfaces sometimes come in handy, especially for making entities "Statusable" when you need it.
I really like this approach, mostly because:
It makes sense :)
Adding and removing options can be done by an admin, via GUI
Easy to filter the appropriate options for the view / controller tiers
Allows re-use of all the helper methods required
Does not clutter your project with mini-classes created for 4 DB rows
I would go with Tom's approach and make the status table look like this :
id
description
isAccepted (bool)
isRejected (bool)
And the values like this :
id : 1
description : New
isAccepted : 0
isRejected : 0
id : 2
description : Accepted
isAccepted : 1
isRejected : 0
id : 3
description : Rejected
isAccepted : 0
isRejected : 1
You then query with isAccepted or isRejected without having to worry about the id.
I recently worked out something similar in a MenuItem entity and three constant 'types'.
The approach I like includes adding some additional 'is' methods. I placed them right in the entity, but that approach is up to you. I'm not sure there is a single 'right way'.
<?php
class Booking
{
const STATUS_NEW = 0;
const STATUS_ACCEPTED = 1;
const STATUS_REJECTED = 2;
protected $status;
public function setBookingStatus($status)
{
$this->status = $status;
}
public function getStatus()
{
return $this->status;
}
public function isStatusNew()
{
return $this->status === self::STATUS_NEW;
}
public function isStatusAccepted()
{
return $this->status === self::STATUS_ACCEPTED;
}
public function isStatusRejected()
{
return $this->status === self::STATUS_REJECTED;
}
}
That allows you to use some easier logic in your Twig templates (YMMV):
{% if booking.statusAccepted %}
{% elseif booking.statusRejected %}
{% else %}
In this way you can keep a similar Twig syntax to your constants.
Imagine you have this class
class Ai1ec_Less_Parser_Controller {
/**
* #var Ai1ec_Read_Variables_Startegy
*/
private $read_variable_strategy;
/**
* #var Ai1ec_Save_Variables_Strategy
*/
private $write_variable_strategy;
/**
* #var Ai1ec_Less_Variables_Collection
*/
private $less_variables_collection;
/**
* #var Ai1ec_Less_Parser
*/
private $ai1ec_less_parser;
/**
* We set the private variables in the constructor. I feel that there are too many parameters.
* Should i use setter instead and throw an exception if something is not set?
*
* #param Ai1ec_Read_Variables_Startegy $read_variable_strategy
* #param Ai1ec_Save_Variables_Strategy $write_variable_strategy
* #param Ai1ec_Less_Variables_Collection $less_variables_collection
* #param Ai1ec_Less_Parser $ai1ec_less_parser
*/
public function __construct( Ai1ec_Read_Variables_Startegy $read_variable_strategy,
Ai1ec_Save_Variables_Strategy $write_variable_strategy,
Ai1ec_Less_Variables_Collection $less_variables_collection,
Ai1ec_Less_Parser $ai1ec_less_parser ) {
}
}
I need those variables to be set and so i set them in the constructor ( but that look like too many parameters ). Another option would be to use setters to set them and then in a method throw an exception if one of the required variables is not set like this
public function do_something_with_parser_and_read_strategy() {
if( $this->are_paser_and_read_strategy_set === false ) {
throw new Exception( "You must set them!" );
}
}
private function are_paser_and_read_strategy_set () {
return isset( $this->read_variable_strategy ) && isset( $this->ai1ec_less_parser );
}
Do you think that one of the two methods is better?And why?
Is your class immutable? If so, then having 100% member population via the constructor is often the best way to do it, but I'll agree it can start to look ugly if you have more than a 5 or 6 parameters.
If your class is mutable then there's no benefit from having a constructor with required parameters. Expose the members via accessor/mutator methods (aka properties).
The factory pattern (as suggested by #Ray) can help, but only if you have a variety of similar classes - for a one-off then you can simply use static methods to instantiate the object, but you'll still have the "too many parameters" problem.
The final alternative is to accept an object with fields (one field for each parameter), but use this technique carefully - if some values are optional then just use method overloading (which unfortunately PHP doesn't support).
I'd just stick with what you're doing and only change it for something else if it presents a problem.
Class naming Controller somehow reflects MVC, or in general - any mechanism responsible for processing sequence.
Data object classes tend to have many fields in any case - it is their responsibility.
Regular object relying on many other objects could be possibly missing a point.
There are four objects, as I see: read, save, parse and provide collection interface to something.
Why shall one have different interfaces for reading and writing? Could this not be combined into one?
Parser shall be a library on itself, thus there may be no reason to combine it anywhere, although it could possible use readers/writers for itself, and, in return, provide collection. Thus could it be possible that parser would take an argument of reader and return a collection object?
That is more about specific case.
In general - having many arguments to the method (or initializing many fields within an object by other objects of different domains) indicates some sort of design flaw.
Kind of on-topic might be this article on Constructor Initialization - it advises to use in-constructor initialization. Just be sure to follow up to the point:
What if there's a lot of collaborators to provide in the constructor? A large list of construction parameters, like any large
parameter list, is a CodeSmell.
And as Ray has written - there is a possibility to initialize using setters, and there is article on that too. To the extent of my view - I think that Martin Fowler really summarizes these cases pretty well.
There is no "better" way. But here are few things you have to consider:
constructors are not inherited
if class requires too many objects, it is responsible for too much
This might have impact on your choice of what sort of interface your class implements.
The general rule of thumb would be this:
If parameters are mandatory for class to function, they should be injected through constructor.
The exception would be, if you initialize the instance by using a factory. It is quite common for factory to build instance form diverse classes, where some of them implement same interface and/or extend same parent class. Then it is easier to inject shared objects through setters.
Creating your objects using factories that call setters instead of using a constuctor of a set number of parameters is much more flexible. Check out the builder and factory patterns.
Throwing exceptions for accessing not fully built objects is good!
Any function that has over 2 (sometimes 3) arguments, I always pass an array, so it would look like:
public function __construct(array $options = array()) {
// Figure out which ones you truly need
if ((!isset($options['arg1'])) || (mb_strlen($options['arg1']) < 1)) {
throw new Exception(sprintf('Invalid $options[arg1]: %s', serialize($options)));
}
// Optional would look like
$this->member2 = (isset($options['arg1'])) && ((int) $options['arg2'] > 0)) ? $options['arg2'] : null;
// Localize required params (already validated above)
$this->member1 = $options['arg1'];
}
Passing an array of options allows for future growth without having to change the function signature. However it does have it's drawback in that the function must localize all elements of the array to ensure access doesn't throw warnings / errors (if an element is missing from the array).
The factory solution is in this case is not a good choice, because you are still left with the problem of passing the values to the factory so it can initialize the object with the correct values.
The standard solution to "too many constructor arguments" is the builder pattern. Your controller class itself will still have a long constructor, but clients can use setters on the builder, which in turn will later call the long constructor.
If you only construct your controller object in one or two places, it wouldn't even be worth all the trouble to create a builder; in that case, just stick with your current code.
How do I perform queries in an entity?
namespace Entities\Members;
/**
* #Entity(repositoryClass="\Entities\Member\MembersRepository")
* #Table(name="Members")
* #HasLifecycleCallbacks
*/
class Members extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="id", type="bigint",length=15)
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #Column(name="userid", type="bigint", length=26, nullable=true)
*/
protected $userid;
/**
* #Column(name="fname", type="string", length=255,nullable=true)
*/
protected $fname;
/**
* #OneToMany(targetEntity="\Entities\Users\Wall", mappedBy="entry", cascade={"persist"})
*/
protected $commententries;
public function __construct()
{
$this->commententries = new \Doctrine\Common\Collections\ArrayCollection();
}
}
Example I would like to have a function inside this entity called: filter()
and I want to be able to filter the commententries collection. It should return a collection with a certain condition such id=1. Basically it should be filtering the data received from the join query.
So something like this:
$this->commententries->findBy(array('id' => 1));
But obviously this does not work.
Your ArrayCollection already implements a filter() method, you need to pass a Closure to get it to work your entities (here, the commentEntries).
$idsToFilter = array(1,2,3,4);
$member->getComments()->filter(
function($entry) use ($idsToFilter) {
if (in_array($entry->getId(), $idsToFilter)) {
return true;
}
return false;
}
);
(not tested)
Note that such method will iterate and eager load over all your Comments, so in case where a User has a lot it may be a big bottleneck;
In most case, you want to use a custom repositories, where you can place such logic.
As timdev suggested, you can create a MemberService which will wrap such call by being aware of the EntityManager.
Separating Entities from the Peristance Layer is a big improvement over Doctrine 1, and you should not break that rule.
Generally speaking, you shouldn't do this.
Entities, as a rule of thumb, should not know about the entitymanager (directly, or via some intermediary object).
The reason for this is mostly testability, but in my experience, it helps keeps things organized in other ways.
I'd approach it by designing a service class that handles the lookups for you. Your controller (or whatever) would drive it like this:
<?php
// create a new service, injecting the entitymanager. if you later wanted
// to start caching some things, you might inject a cache driver as well.
$member = $em->find('Member',$member_id); //get a member, some how.
$svc = new MemberService($em);
$favoriteCommentaries = $svc->getFavoriteCommentaries($member);
As I hint in the comment, if you decide later that you want to add caching (via memcached, for instance) to avoid frequent lookups, you'd do that somewhere near or in this service class. This keeps your entities nice and simple, and easily testable. Since you inject your entitymanager into the service at construction-time, you can mock that as needed.
getFavoriteCommentaries() could use various implementations. A trivial one would be to proxy it to Member::getFavoriteCommentaries(), which would actually load everything, and then filter out the "favorite" ones. That probably won't scale particularly well, so you could improve it by using the EM to fetch just the data you need.
Use a custom repository for queries
You should not write queries in your entities, but you should use a repository for that. This is also explained in the doctrine documentation 7.8.8 Custom Repositories. It will allows you to build your custom queries on a central spot and keeps your entity definitions clean.
Use criteria to filter collections:
But if you want to filter inside your collection in a get method you can use Criteria. You can read on how to use Criteria in the Doctrine documentation 8.8 Filtering collections. To filter like you want to do would look something like this:
Declare at the top of your entity class the Criteria class
use Doctrine\Common\Collections\Criteria
In your getCommentEntries method use the class to filter:
public function getCommentEntries()
{
$criteria = Criteria::create()
->where(Criteria::expr()->eq('id', 1));
$filteredCommentEntries = $this->commententries->matching($criteria);
return $filteredCommentEntries;
}
Your question was really hard to understand, please try and work on how you structure your questions in the future. For instance, you say "return back the same result" but "filter", which could mean anything. Do you want to use the same result set (why on earth would you ever choose to do that), and just use array_filter or array_walk to filter the results or do you actually want to use a conditional join? It's incredibly ambiguous.
Anyway.. answer ( after reading your question 4 times).
$q = $qb->select ( "m","w" )
->from ( "Members", "m" )
->leftJoin( "m.commententries","w",'WITH', 'w.id = :id')
->setParameter ( "id", $id )
->getQuery ();
I agree with "timdev". You shouldn't define query in your entities class. My way to define a service class support the entities are repository classes. For example: User (entity -- YourBundle/Entity/User.php) will have UserRepository (service class -- YourBundle/Repository/UserRepository.php). Your "filter" method should be in here. You just need to map this service class in your entity class. In your controller, you can always access the "filter" via its repository. It's documented very detail in the Symfony2 book
For a standard method, I know one can specify return type in the comments such as:
/**
* Load this entity from the database with the specified primary key.
* #param int $Key
* #return BaseEntity
*/
public static function Load($Key)
{ ... }
I would like to have the return type change depending on the subclass. Something like:
* #return __CLASS__
In other languages, this could be accomplished using templates etc. Do I have any options for PHP?
No. Not unless you code something to generate this code.
Those are comments, they will not change the way that the code runs. They may be used by your IDE for code hints, or by PHPDoc to generate documentation. Those applications may have ways of doing something like what you want, check their documentation. The comments will not, however, affect the way your code runs.
You want to extend your BaseEntity with, say, SubclassEntity and tighten up the interface, so to speak. But, tightening your interface is breaking your interface.
Conceptually, Load should always return a BaseEntity, even if you happen to know it's really a SubclassEntity. In reality, if you know the returned object is a SubclassEntity, then just add this after:
$obj = $container->Load(123); /* #var $obj SubclassEntity */