Alright so I'm trying to make some sense of all these patterns.
Alright, so I'm coding an applicantion in CodeIgniter which needs to be able to send data about a car and a customer to different types of companies using SOAP, maybe XML, comma-separated and so on.
But they all need the same thing.
I wanna make it as dynamic as possible and make sure it's easy to write tests.
So the service should take a couple of things:
a handler
applicants [1-2]
params
object
I started up creating different classes
Gr8Exp
NordCar
SwePerf
each implementing the interface iServiceRequest
interface iServiceRequest{
/**
* Send the request to the company server.
*/
function sendRequest();
/**
* Saves the response into the database.
*/
function saveResponse();
/**
* Prepares the request to the company, setting info from form and shit.
*/
function prepareRequest();
/**
* Soap, XML, CSV, JSON
* #param type $method
*/
function setRequestHandler(iServiceRequestHandler $handler);
}
Then they need to structure up the Soap, XML, CSV, JSON request depending on what handler i put in.
After those who needed to be validated (not all did) I used:
interface iAdaptServiceRequest{
/**
* Structure the array information and put it into an object structure in the right place.
*/
function structure(array $info);
/**
* Make all the checks for the function
*/
function validateInfo();
}
But I'm stuck, It worked really good when I just used SOAP request; but now. Since I need to format them differently, use a different handler for each type of request or company I don't know what to do.
I could put them i different folders and recreate the class in the different folders. But that's not a good practice since I'm duplicating code all over.
In the end I want to run some chaining like this:
$result = $m->prepareRequest()->sendRequest()->saveResponse();
Any suggestions??
IMHO:
-- create/use a front controller.
-- The front controller determines which request handler to use (JSON, SOAP, XML, etc).
-- The request handler generates a common "Request" object that behaves the same across all interfaces, basically putting variables into a common named format inside a "Request object"
-- It determines which service to send the request to and sends the request object there
-- The service processes the request object and generates a response object
-- The controller creates an appropriate (JSON/SOAP/XML) View object to process the response object into the correct view type and the View outputs your response as that type.
I would use something like yours: $result = $m->prepareRequest('JSON')->sendRequest()->saveResponse();, but specifing what format of data I'm sending.
The method prepareRequest(string $type) would check the format and call another method to convert your data to the respective format.
Something like this:
function prepareRequest(string $type){
if ($type == 'json'){
$this->convert2json();
}
if ($type == 'xml'){
$this->convert2xml();
}
// And so on
}
There is often confusion about the MVC or Observer pattern. This is not a situation in which this pattern is applicable. In the MVC pattern are the View and Model related to one another. The View must update itself based on information of the subject. A view and the underlying tables in a database are a good example. That is not what you want here.
The design pattern which suits this problem is the Builder pattern. The Builder pattern consists of four cooperating classtypes:
1. a Builder,
2. a ReaderManager,
3. a ConverterManager, and
4. a DataObject.
The ReaderManager is using the Interpreter pattern. Conversion can be done using the State pattern. What is the output of the ReaderManager (some DataObject) is the input for the ConversionManager. That can be done using an abstract class instead of an interface (my preference for data focused classes). The Builder connects the ReaderManager with the ConverterManager and takes care of the transport of data.
Some years ago I wrote about design patterns. The builder pattern was one of the patterns I described and this is the link to that page:
http://www.loekbergman.nl/InsideArchitecture/TheProcess/DesignPatterns/Builder
It shows a UML diagram of the pattern.
In the next link you can download a jar with some examples of design patterns. One of them the builder pattern:
http://www.loekbergman.nl/InsideArchitecture/DownloadsAndLicense
I have written this code several years ago, therefor do I give you this code without warranty. (Is that the correct term in this context?)
In the code you can see a folder with the name specifications. That is another example of the Interpreter pattern. (In the Builder pattern there is of course also an example of this pattern).
To be complete is here the link to the MVC - pattern:
http://www.loekbergman.nl/InsideArchitecture/TheProcess/DesignPatterns/Observer
and the Interpreter pattern:
http://www.loekbergman.nl/InsideArchitecture/TheProcess/DesignPatterns/Interpreter
Related
(There is a TL;DR: at the bottom)
I have a PDF produced via MVC pattern. I am working with an existing code, which was a bit of a mess, but now I am seeing a pattern emerge.
Currently, I have a Controller class, and inside I have many many separate functions, roughly one function per page. Each function does something like this:
function showPage()
{
//get some data from repository
$data1 = $this->repository->getData1();
$data2 = $this->repository->getData2();
//pass that data to the PDF API class, aka "the view"
//and the class takes care of creating PDF pages
//with the appropriate data
$this->pdfApi->showView($data, $data2);
}
The above achieves a clean separation between Repository (which only returns data), the PDF API service (which receives the data and doesn't need to care or maintain data retrieval constructs. And Controller which pretty much just asks for Data, and passes it to PDF API. And all was well until I came across this problem:
Problem
Most every page has a "footer" with a message, and a "Proposal Number" that needs to be displayed on the page. Sometimes it also has other pieces of data. Since PDF API class has no data in itself, someone has to pass that data to PDF API. I have been passing the above to pieces of information every time as part of function parameters but it became inconvenient -- there are too many parameters to pass and they are cluttering up the code.
Try at Solution
To reduce the clutter in parameter passing, In my Controller I have created pulled data (via Repository) for variables such as $footerText and $proposalNumber and then used them I populate PDF API's own class properties. Side-effect of this is that now my PDF API has the relevant bits of data embedded directly in the API (which I consider to be undesirable, since data layer now imposes into API class)
So far I have resisted the temptation to just pass the entire Repository object to PDF API because that will do very much the same - mix data layer and API Layer, plus, API layer will have unrestricted access to Data, which can also be undesirable.
Actual Problem
When I want clean layer separation, my code is cluttered with multiple function parameter passing.
When I pass the entire Repository to my API class, I mix data and API layers, and API layer gets too much freedom to use Repository class.
Can I somehow achieve layer separation without the clutter or "mixing layers" issues identified above?
If you like to see code, here is some code below of my various unsuccessful tries :)
TL;DR: My various unsuccessful tries to keep layers separate or to reduce clutter proved to be unsuccessful
//in Controller - Exhibit 1
//Separation achieved with only data parameter passing tying layers together
//but, too much clutter -- too many parameters
//maximum layer separation but lots of annoying data passing
$data1 = $this->repository->getData1();
....
$data24 = $this->repository->getData24();
$this->pdfApi->showView($data1, $data2, $data3, ... );
//in Controller - Exhibit 2
//Layers are mixed - my data is now tied into API
//in constructor
$data1 = $this->repository->getData1();
....
$data24 = $this->repository->getData24();
$this->pdfApi->setData1($data1);
$this->pdfApi->setData24($data24);
//inside function (API already has data as part of its own vars):
$this->pdfApi->showView();
//in Controller - Exhibit 3
//layers are mixed -- entire Repository got into my API
//in constructor
$repo = new Repository();
$this->pdfApi->setRepository($repo);
//inside function (API has full Repository access gets its own data and more):
$this->pdfApi->showView();
I think the Exhibit 1 is most correct.
//inside Controller
$data = array(
'data1' => $this->repository->getData1(),
//...
'data24' => $this->repository->getData4()
):
$this->pdfApi->showView($data);
I say this because a popular framework, ZF2, which I use, also ascribes to the same pattern.
//inside Controller
$data = array(
'message' => 'Hello world',
);
$view = new ViewModel($data);
In my case the View is the PDF API
Using MVC I have something like this:
class Controller
{
//returns View
function indexAction()
{
$data = $this->getData($this->id);
$view = new ViewModel();
$view->setVariable('data' => $data);
//used to render HTML template + data above later on
return $view;
}
//gets data from DB
//currently also does business-proprietary computation on data
function getData($id)
{
//repository/dao pattern
$data = $this->repository->getData($id);
//Business Logic "derivation"
foreach ($data as $datum)
{
//that does not go into "Controller
//that does not go into "Repository"
//but where does it go? - that's my question
$derivedData[] = (new Business())->doLogic($datum);
}
return $derivedData;
}
}
Recap
I used Controller to get my data out of DB using Repository pattern, then placed received data into view. But business-related computations are left stranded.
Question
Where do I place my business logic computations that act on the data gotten from repository? The derived data which is to return to Controller later, to be placed into View?
My personal choices of architecture are usually to:
Have small controllers as thin as I can, doing only session and general right checking
Services that are handling all business logic, one (one classe yes) per potential feature I need
Services are querying repositories, and eventually manipulate the data in and out, but usually no Controller, nor view will do a ->save() anywhere.
This means that those services are usually designed to be independent from the database and easier to be tested because they only take care of one and only one task.
In your example, the whole function getData will be a service that I would call GetCarDataById. This assuming that you manipulate Cars, I don't like to leave data wandering alone.
EDIT: to make it clear, this kind of approach is not MVC to some definition, most people interpret MVC as putting all code either in controller, either in repositories (model). To others view, MVC doesn't mean that you have other classes, what I call services, and actually most of my code lives here.
The MVC pattern is clear for me.
From wikipedia:
The model directly manages the data, logic and rules of the
application. A view can be any output representation of information,
such as a chart or a diagram. Multiple views of the same information
are possible, such as a bar chart for management and a tabular view
for accountants. The third part, the controller, accepts input and
converts it to commands for the model or view.
Answering your question. The modifications goes in the model domain.
Using Symfony, I am displaying a table with some entries the user is able to select from. There is a little more complexity as this might include calling some further actions e. g. for filtering the table entries, sorting by different criteria, etc.
I have implemented the whole thing in an own bundle, let's say ChoiceTableBundle (with ChoiceTableController). Now I would like to be able to use this bundle from other bundles, sometimes with some more parametrization.
My desired workflow would then look like this:
User is currently working with Bundle OtherBundle and triggers chooseAction.
chooseAction forwards to ChoiceTableController (resp. its default entry action).
Within ChoiceTableBundle, the user is able to navigate, filter, sort, ... using the actions and routing supplied by this bundle.
When the user has made his choice, he triggers another action (like choiceFinishedAction) and the control flow returns to OtherBundle, handing over the results of the users choice.
Based on these results, OtherBundle can then continue working.
Additionally, OtherOtherBundle (and some more...) should also be able to use this workflow, possibly passing some configuration values to ChoiceTableBundle to make it behave a little different.
I have read about the "Controller as Service" pattern of Symfony 2 and IMHO it's the right approach here (if not, please tell me ;)). So I would make a service out of ChoiceTableController and use it from the other bundles. Anyway, with the workflow above in mind, I don't see a "good" way to achieve this:
How can I pass over configuration parameters to ChoiceTableBundle (resp. ChoiceTableController), if neccessary?
How can ChoiceTableBundle know from where it was called?
How can I return the results to this calling bundle?
Basic approaches could be to store the values in the session or to create an intermediate object being passed. Both do not seem particularly elegant to me. Can you please give me a shove in the right direction? Many thanks in advance!
The main question is if you really need to call your filtering / searching logic as a controller action. Do you really need to make a request?
I would say it could be also doable just by passing all the required data to a service you define.
This service you should create from the guts of your ChoiceTableBundleand let both you ChoiceTableBundle and your OtherBundle to use the extracted service.
service / library way
// register it in your service container
class FilteredDataProvider
{
/**
* #return customObjectInterface or scallar or whatever you like
*/
public function doFiltering($searchString, $order)
{
return $this->filterAndReturnData($searchString, $order)
}
}
...
class OtherBundleController extends Controller {
public function showStuffAction() {
$result = $this->container->get('filter_data_provider')
->doFiltering('text', 'ascending')
}
}
controller way
The whole thing can be accomplished with the same approach as lipp/imagine bundle uses.
Have a controller as service and call/send all the required information to that controller when you need some results, you can also send whole request.
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$responeFromYourSearchFilterAction = $this->container
->get('my_search_filter_controller')
->filterSearchAction(
$this->request, // http request
'parameter1' // like search string
'parameterX' // like sorting direction
);
// do something with the response
// ..
}
}
A separate service class would be much more flexible. Also if you need other parameters or Request object you can always provide it.
Info how to declare controller as service is here:
http://symfony.com/doc/current/cookbook/controller/service.html
How liip uses it:
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service
I'm working with a PHP MVC Framework. Works really well. I like the separation of the business layer (model) with the business logic (controller). But i just stumbled upon a problem. Here's the thing:
Suppose i navigate to the following url:
http://localhost/user/showall/
In this case the userController.php is called and within that file there is a method showallAction() which gets executed.
In the showallAction() method i simply do a request to a model which gets all the users for me. Something like this:
public function showallAction()
{
// create userModel object
$users = new userModel();
// get all users and assign the data to a variable which can be accessed in the view
$this->view->users = $users->getAllUsers();
// render views
$this->view->render();
}
So this method gets all the users, assigns the data returned from the userModel to a variable and i can easily work with the returned data in my view. Just a typical MVC thing.
Now here comes the problem.
I also need to create a native iphone variant. Ofcourse the looks will be totally different. So all i actually want to do is to request this url:
http://localhost/user/showall/
And that it just gives me the array (in json format) back. So i can use that for the mobile development.
But this obviously can't be done right now because the showallAction() method assumes that it is for web browser display. It doesn't echo JSON formatted, instead it simply assings the array of users to a variable.
So that means i have to create another method "showallMobileAction()" in order to get the data, but specifically for the mobile device. But this is not an elegant solution. I'm sure that are better ways...
Anyone any idea how can i solve this problem??
In your situation i would modify the routing mechanism.
It would be useful, if you could add extension at the end of URL, which represents the format you expect, like :
http://foo.bar/news/latest >> HTML document
http://foo.bar/news/latest.html >> HTML document
http://foo.bar/news/latest.rss >> you RSS feed
http://foo.bar/news/latest.json >> data in JSON format
It's a simple pattern to recognize. And you can later expand this to add .. dunno .. pdf output, or Atom feeds.
Additionally , two comments :
Model is not a type of objects. Instead it is a layer, containing objects responsible for business logic, and objects responsible for data storage/retrieval.
View should be a full blown object, to which you bind the domain objects (objects responsible for business logic).
You could pass parameters to your url:
/user/showall/json
and get the third URL segment with a custom function or a built-in one. For instance, with CodeIgniter: $this->uri->segment(3).
Some frameworks will pass the additional parameters to your method. Just try this with the URL I wrote above:
public function showallAction()
{
print_r(func_get_args());
}
I'm not familiar with PHP MVC but in general terms I'd use the "accepts" HTML header field to request the response in either "text/html" or "text/json", the controller would check for the accepts type and return the response accordingly.
How do people design their service layer interfaces?
I'm programming a large web application (in PHP) and we're using MVC, and programming thin controllers e.g. (pseudo code follows)
public savePersonAction() {
$input = filter($_GET);
... input validation ...
$result = $this->_service->savePerson( ? );
... etc
}
Should savePerson in the service take an argument of the entire $input structure or context (in PHP, an associative array)?
E.g. this -
public function savePerson(array $input) {
or should one separate out all the input fields and provide a "hard" interface e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth, ... etc.. for many more) {
Thanks.
Paul
If you're going to follow the MVC spirit your savePerson method should not accept the raw input. It shouldn't be directly coupled with the format of the data coming from the ui. Instead it should accept input defined in the terms of your service domain, like a "person" object. (This could just be an associative array like Cobby suggested). It would be the job of the controller's action method to map the raw input to the format required by the service.
The benefit of this extra translation step is that it isolates your service (model) from the ui. If you implement a different ui, you don't need to change the service interface. You just need to write new controllers (and views, of course).
While your example like savePerson($title, $firstName, $lastName...) is the right idea, it's usually a bad sign when you have methods with more than 2 or 3 arguments. You should be able to group related arguments into some kind of higher-level object.
My MVC applications are structured like this:
Controller -> Service -> ORM/other library
To answer your question, typically in your controller you will be getting form data as an array, i.e. $form->getValues() or something similar. In terms of maintainability it's best if your Services except arrays as arguments, that way if you add another field to a form, you only have to update the form and the service, your controller can remain untouched and still work.
So I think go with your first example:
public function savePerson($personArray);
Furthermore, you shouldn't need a "hard" interface because your form library will take care of validation/filteration/sanitization so we can assume that the associative array will be valid, plus the method definition will get ridiculously long with named parameters.
I would separate out all the input fields and provide a "hard" interface in Service e.g.
public function savePerson($title, $firstName, $lastName, $dateOfBirth) {...}
Its cleaner and there are no assumptions needed.