When it comes to the location of server side filtering and validation, there are two obvious options: the Service Layer or the Domain. The benefit of the first approach is that it is easy to manage. It is easy to spot when when filtering and validation logic is missing from the Service Layer, and it's straightforward to fix.
The second option, using the Domain, is more difficult to manage but better aligned with OO principles. For this approach, I see three main data contexts.
Data for newly created objects can be filtered and validated in the object's factory.
Data which updates object properties can be filtered and validated in the custom setter methods.
Data which is not persisted usually comprises finder parameters and should be filtered and validated in the mapper methods themselves.
You may choose the first option, or the second, or a combination of the two. I get away with filter_var for simple cases, and Zend Input Filters for more advanced scenarios.
The Getting Started part of this discussion will get you up an running with the Starter Application. If you are planning to use it at the basis of your own application the following steps may be helpful.
Fully articulate the business problem
Identify the parties who have an interest in the solution. List their requirements. Think of the parties as ‘actors’ and the requirements as ‘use cases’
Outline a solution, paying particular attention to the nouns. Think of nouns as Domain objects
Build a small number of Domain objects first, starting with those that are most central to the application
Specify the Domain object interfaces
Create the Domain object classes and add property names to $allowed
Create a proxy for each domain object
Setup collection handling for each Domain object
Specify the collection interfaces
Create the collection classes
Create a proxy for each collection
Implement public setters on Domain objects for all writable properties
Implement behaviour methods on Domain objects as required
Create a factory for each Domain object, overriding base methods as required
Create unit tests that cover Domain objects, factories, collections and proxies
Design the database
Configure the Zend DB adapter service
Declare mapper interfaces in Core\iMapper including method signatures
Create and implement Mapper classes
Create test data
Create unit tests that cover Mapper classes
Group use cases based on related functionality
Create a Service Layer service for the main use case groups
Create a listener for each Service Layer service
Implement an action for each use case
Trigger events where required, and implement and bind handlers in listeners
Create tests that cover Service Layer classes
Create controllers and views according to UI requirements, tools and approach
Implement controller actions to access Service Layer methods
Create functional tests with Selenium or other tool
Demonstrate your system to business stakeholders
Revisit each of the steps above, as many times as required, revising, improving, completing
Continuous integration and deployment will be valuable companions on the journey.
The N+1 selects problem can occur wherever a Domain object has a multi-valued property. Client code triggers a select query on the database to get hold of a first object, then a further 'N' selects to get hold of each member of the multi-valued property.
In the case of the Starter Application, most of the problem is taken care of in the object factories. The factory that creates the first object sets its multi-valued properties to collection proxies (in doAddRelations). In the diagram below for example, the Foo factory sets a Foo::Bars property to a Bar collection proxy. If/when the multi-valued property is accessed, data for the entire collection is retrieved in a single query.
This works fine for simple two-level, 'parent/child' relationships, however the situation becomes more complex when additional levels are introduced. Where three levels of objects are accessed, the factory that creates the first object must use special collection proxies for multi-valued properties, proxies which not only load child data, but also 'eagerly load' data for objects related to those children.
Eager loading can become unwieldy in complex object graphs. If you are regularly working more than three levels deep it may be time to consider an ORM or alternative database access patterns.
In the case of the diagram above, if Foo is loaded in order to access Baz or the Bats, the N+1 problem will occur. To avoid this, client code must forewarn the Foo factory that it intends to access Baz and Bat, and the Foo factory must then attach a Bar collection proxy that eagerly loads Baz and Bat data. I use 'flavours' to let the factory know when it should eagerly load data.
The possible flavours of a factory are stored as class constants in the factory itself, where the value of each constant is a bitmask. The actual flavour of a factory instance is stored as a bit field in $factory->flavour, and can be modified via the fluent methods setFlavour and addFlavour. (Note: if, for some reason, your service locator returns a new instance of a factory each time it is called, you will need to use a static factory::flavour.)
The Foo factory needs to be updated with conditional code that sets the correct proxies based on the current factory flavour. The changes are limited to doAddRelations, and the updated method is as follows:
Of course this will have a knock-on effect on the Bar mapper which must be updated with three new finder methods: findByFooWithBazAndBats, findByFooWithBaz, findByFooWithBats.
These new methods are more complex than other mapper methods. You will need to decide whether to join tables and bring data back in one query, or run multiple queries, and you will need to manipulate arrays and sub-arrays. It will help if you have mastered PHP array functions. Closures are often useful here too.
Note that BarFactory::doTypeConversion is already set up to handle arrays as inputs for the Baz and Bats properties, and is therefore ready to work with the multi-dimensional arrays produced by the new Bar mapper methods. I have found that it is a good policy to build factories that handle arrays as inputs for all properties that are objects or collections. In most cases they will eventually come in handy.
If you are comfortable with the eager loading approach, but not the proliferation of Mapper methods, you could consider a solution that uses a single mapper method to load the Bar collection with all direct associates, something like BarMapper::findByFooWithAssociates.
A Note on Collections
I started this section by saying that the N+1 problem can occur wherever a Domain object has a multi-valued property, but there is a second form of the problem too. The second form occurs when client code, usually the Service Layer, calls a mapper method which returns a collection, and each member of the collection has a proxied property. If the client iterates the collection, accessing the proxied property along the way, the result is N trips to the database.
The obvious solution to this is to add mapper methods that eagerly load data for properties which were proxied, and let the Service Layer call these instead of the more simple finders. In my experience, these additional mapper methods are often one-and-the-same as the methods required for eager loading in the first form of the problem, which is convenient.
CSS has a rich and powerful syntax that seems natural to developers. Diving in headlong however can quickly lead to a world of unintended consequences, tangled stylesheets, and an escalating specificity war.