Injecting an arbitrary parameter to a service - symfony

I'm wondering if there is a sort of best practice for the following case.
For instance, I have several services and inject them all as an array into a "factory" service. Then I call a method of this factory and want to get only one service depends on some conditions. After that I execute this service and get a result...
However, some of these services require a random string that I get from a client's request.
Of cause, I can call a service's method with this string as a parameter but several services do not require this string and I'll get "unused variable" in the method.
I guess that I could get service from the factory and then call a setter to add this string into the service. But it does not look like a stateless service.
Is there a more elegant solution to pass parameters that I could not inject into service nor use the setter for it?
Here how it looks in my code
First, I have an interface of all servers that I want to check. The service should support a customer an then it should render information from a DTO.
interface Renderable {
public function supports(Customer $customer);
public function render(CustomerDTO $dto);
}
Next, I have several services. This one uses DTO to render data.
class ServiceOne implements Renderable
{
public function suppots(Customer $customer)
{
return $customer->getPriority() === 1;
}
public function render(CustomerDTO $dto)
{
return 'One: '.$dto->getName();
}
}
However, some services do not need any DTO to render, they just provide a hardcoded value.
class ServiceTwo implements Renderable
{
public function suppots(Customer $customer)
{
return $customer->getPriority() !== 1;
}
// service does not use DTO, it simply output result
// so, I'll get a notice about unused variable
// and I can not remove it from the method since it is in interface
public function render(CustomerDTO $dto)
{
return 'Two';
}
}
This is a factory. It has all services injected as an array. Then it checks and returns the first service that supports a customer instance.
class ServiceFactory
{
/** #var Renderable[] */
private $services;
public function __construct(iterable $services)
{
$this->services = $services;
}
public function getRenderer(Customer $customer)
{
foreach ($this->services as $service)
{
if ($service->supports($customer)
{
return $service;
}
}
}
}
Here like I use factory and its result
$customer = ...; // it comes from a database
$request = ...; // it comes from a http request
$renderService = $factory->getRenderer($customer);
$customerDTO = CustomerDTO::createFromData([
'customerUid' => $customer->getUid(),
'date' => new \DateTime(),
'name' => $request->getSheetUid(),
'tags' => $request->getTags(),
]);
$renderService->render($customerDTO);
So, I have to call Renderer::render with a DTO instance. But some services do not use it to "render" data. I also can not inject it into a renderer service since this object (DTO) is built in a runtime when all services already injected. I also can not inject a RequestStack into service.

Since your parameter came from request - it can't be directly injected into service. Depending on actual logic of your services you can consider one of approaches listed below. Let's call your "random string that came from a client's request" a $requestParam for further reference.
In both cases you will need to get your $requestParam from actual Request object and pass it somewhere else. It can be done in a different ways, I would propose to create listener (e.g. RequestParamListener) for kernel.request event and put here a piece of code that takes parameter from Request and pass it further into this listener. Into approaches listed below I will assume that $requestParam will be passed in this way.
1. Separate provider
You can create separate class (e.g. RequestParamProvider) that will act as provider of this $requestParam for other services. It will receive $requestParam from RequestParamListener and other services that needs to get $requestParam will need to inject this provider and use its getRequestParam() method to obtain required parameter.
From my point of view it is the simplest approach and I would recommend it.
2. Direct injection by factory
Since you have some factory service - you can pass this $requestParam directly into factory and let it to initialize other services. Less flexible because you will need to implement initialization logic by itself and maintain it while project evolves.
3. Direct injection using interface
You can create separate interface (e.g. RequestParamAwareInterface) that will contain setRequestParam() method and let all classes that needs this $requestParam to implement this interface. After that you will need to write separate compiler pass that will collect all such classes (by iterating over ContainerBuilder and looking for implementation of particular interface by class inside service's definition) and pass array of these services to your RequestParamListener. Listener in its turn will be obligated to pass $requestParam for each of given services.
This approach will let your application to grow without need to sync $requestParam injection logic. However it will came at a cost of preliminary instantiation of all affected services regardless of actual further use of created instances.

Related

springboot+mybatis, About Native Dao development

I'm trying a new development method.
In mybatis3, I write mapper.java and mapper.xml usually.
I know, the sql statements is corresponded by sqlId(namespace+id).
I want to execute the sql statement like this :
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
but I get a error:
Cause: java.lang.IllegalArgumentException: Mapped Statements collection does not contain value for mapper.JinBoot.test
at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:150)
at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)
at cn.tianyustudio.jinboot.dao.BaseDao.select(BaseDao.java:20)
at cn.tianyustudio.jinboot.service.BaseService.select(BaseService.java:10)
at cn.tianyustudio.jinboot.controller.BaseController.test(BaseController.java:21)
here is my BaseDao.java
public class BaseDao {
private static SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
public static List<Map> select(String sqlId, Map param) {
try {
factoryBean.setDataSource(new DruidDataSource());
SqlSessionFactory sessionFactory = factoryBean.getObject();
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession.selectList(sqlId, param);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
here is UserMapper.xml
<mapper namespace="mapper.JinBoot">
<select id="test" parameterType="hashMap" resultType="hashMap">
select * from user
</select>
</mapper>
the application.properties
mybatis.mapperLocations=classpath:mapper/*.xml
I start the project, the send a http request, after controller and service ,the param 'sqlId' in BaseDao is 'mapper.JinBoot.test' (see error info).
In method 'BaseDao.select', both the parameter and the result type is Map.
So I don't want to create UserMapper.java, I want try it.
How can I resolve it? What's the malpractice of this way?
This does not work because spring boot creates its own SqlSessionFactory. And the option in application.properties that specifies where mappers should be looked for is only set for that SqlSessionFactory. You are creating unrelated session factory in your DAO and it does not know where to load mappers definition.
If you want to make it work you need that you DAO is spring managed so that you can inject mybatis session factory into it and use it in select. This would also require that you convert select into non static method.
As I understand you want to have only one method in you base DAO class and use it in individual specific DAO classes. I would say it makes little sense. If the method returns Map there will be some place that actually maps this generic type to some application specific types. This would probably be in the child DAOs. So you still need to create the API of the child DAO with the signature that uses some input parameters and returns some domain objects. And that's exactly what you want to avoid by not creating mybatis mapper classes.
The thing is that you can treat your mytabis mappers as DAOs. That is you mappers would be your DAOs. And you don't need another layer. As I understand now you have two separate layers - DAO and mappers and you want to remove boilerplate code. I think it is better to remove DAO classes. They are real boilerplate and mybatis mapper can serve as DAO perfectly. You inject it directly to you service and service depends only on the mapper class. The logic of the mapping is in the mapper xml file. See also answer to this question Can Spring DAO be merged into Service layer?

Best place to implement a method that return response in Symfony2/MVC

I got a method able to create a CSV file thanks to StreamedResponse object of Symfony2 framework. I use the method several times so I put a callback parameter to personalise the behavior (I forget the buzz word for this practice in Object-Oriented Programming).
Where is the best place to put this method in a MVC project?
Repository? (Model/DAO/Manager)
Entity? (POPO)
Controller
Service
Through a interface (This object able to create CSV file)
Other
As your logic returns a response, the most adapted context is a controller.
Also, if your logic is called from multiple contexts or by multiple classes of the same context (e.g. controllers), to avoid duplicated code, you have two possibilities (at least) :
1 - Use an AbstractController and make your controllers extends the abstract.
2- Use a service (i.e. CsvManager).
If you want some example implementations, see Symfony2 reusable functions in controllers and the Controller as a service chapter of the Symfony documentation.
An example of service implementation:
// src/AppBundle/Services/CsvManager.php
class CsvManager
{
public function generate(/** params */)
{
// Return your streamed response
}
}
The service declaration :
// app/config/services.yml
services:
# ...
app.csv_manager:
class: AppBundle\Services\CsvManager
Now, you can use the service from all your controllers and other contexts that implements the services container. example:
// src/AppBundle/Controller/TestController.php;
class TestController extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
public function printCsvAction()
{
$csvManager = $this->get('app.csv_manager');
return $csvManager->generate(/** params */);
}
}

Proper way to test a Symfony2 Service with Doctrine

I'm struggling to find the correct way to unit test my symfony 2 services which use doctrine or other common services.
What i have done so far:
In my understanding the controller actions should:
be as short as possible
take the request
execute required methods from injected services
build a response out of this
is a service itself
To accomplish a lightweight action, i try to encapsule the logic into a separate service which gets injected into the controller.
This works nicely expect for testing everything.
Here my current code:
Controller
class SearchController
{
// search_helper, request and templating are controller-injected
protected $search_helper;
protected $request;
protected $templating;
// ...
public function searchAction()
{
$searchterm = strtolower($this->request->query->get('q'));
$result = $this->search_helper->findSamples($searchterm);
// Found a single result. Redirect to this page
if (is_string($result))
{
return new RedirectResponse($result, 301);
}
return new Response($this->templating->render('AlbiSampleBundle:Search:index.html.twig', array('results' => $result)));
}
}
SearchService
class SearchHelper
{
// doctrine, session and min_query_len are controller-injected
protected $doctrine;
protected $session;
protected $min_query_len;
// ...
public function findSamples($searchterm)
{
if (strlen($searchterm) < $this->min_query_len)
{
$msg = 'Your search must contain at least 3 characters!';
$this->session->getFlashBag()->add('error', $msg);
return false;
}
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
// Execute a more advanced search, if std. search don't delivers a result
// ...
return $results;
}
}
How can i test this code correctly?
The repository is tested with phpunit_db and a inmemory sqlite database ✓
The action can be tested through a simple functional test ✓
What's left is the logic in the search-service. e.g. the findSamples method
My first thought was to mock the dependencies (in fact that was one of the main aspects in separating the dependencies), but you not only have to mock the doctrine object, but also the entitymanager and the repository.
$em = $this->doctrine->getManager();
$results = $em->getRepository('AlbiSampleBundle:Sample')->findPossibleSamples($searchterm);
I think there must be a better solution. Not only would this mocking need many LOCs, it also doesn't feel right. The test would be unnecessarily coupled really tight to the SUT.
EDIT
Here is a sample test i came up with. Using mock objects.
The test won't work. I realized it would take much more mock-objects and i got the feeling this isn't the right way.
The test fails because SessionMock->getFlashbag doesn't return a flashbag with add method.
doctrine->getManager returns no EntityManager. The EntityManager has no getRepository method. And the repository is missing findPossibleSamples.
class SearchHelperTest extends \PHPUnit_Framework_TestCase
{
private $router;
private $session;
private $doctrine;
public function setUp()
{
parent::setUp();
// ...
}
public function testSearchReturnValue()
{
$search_service = $this->createSearchHelper();
$this->assertFalse($search_service->findSamples('s'));
}
protected function createSearchHelper()
{
return new SearchHelper($this->doctrine, $this->router, $this->session, 3);
}
protected function getDoctrineMock()
{
return $this->getMock('Doctrine\Bundle\DoctrineBundle\Registry', array('getManager'), array(), '', false);
}
protected function getSessionMock()
{
return $this->getMock('Symfony\Component\HttpFoundation\Session\Session', array('getFlashBag'), array(), '', false);
}
protected function getRouterMock()
{
return $this->getMock('Symfony\Component\Routing\Router', array('generate'), array(), '', false);
}
}
Hope the community can help me, writing well tested code :)
cheers
For your specific example I would argue that the validation of the $searchterm doesn't really belong in your service - at the very least a service should never depend on the session. There are ways you could move the session out of the service and leave the validation in but personally I would use symfony validation for this i.e. have a SampleSearchType for the form that uses itself as the data class and hang the validation off that in validation.yml (or using annotations as appropriate).
Once that validation is taken out, what's left from your question is another findX() method to be added to the repository (there's no reason why repository methods can't call and build on each other) which you already know how to test.
Having said that, I still agree that with Symfony there is a general issue of how to test services in isolation from injected services. With respect to testing in isolation from the persistence layer I've avoiding trying to do this so far. My business layer services are so tightly coupled with the persistence layer that the cost of trying to test them independently is not worthwhile (what logic there is consists mainly of making related db updates or sending emails for which symfony provides it's own decoupling mechanism). I'm not sure if this is because I'm doing it wrong or because the apps I'm working on are light on business logic!
To isolate service tests from dependencies other than persistence I've tried:
Overriding service classes with mocked versions in the configuration. Issue - you don't want to do this for functional tests which means you have to have tests scripts which update the configuration and/or change the config to run individual tests. Advantage - you can run the same test as an isolated unit test and as an integration test by flipping the config
(Warning: nasty hack!) providing a setter method to replace an injected service with a mocked version from the test program.
(Not yet tried) Directly instantiate the service being tested, passing mock dependencies in on construction.
With respect to isolating from the persistence layer the only approach that makes sense to me is to abstract it out of the service to be tested into a wrapper service which contains no additional logic. The wrapper service could then be mocked using one of the above approaches (or hopefully a better solution that someone else is going to suggest?!)
EDIT: to address the issue of complexity of mocking dependencies - very occasionally this may be unavoidable but in general this is an indication that the design needs revisiting. This is one of the strengths of TDD - it strongly encourages simplified design and decoupling of components:
No service should need to be dependent upon the session object. This is not good practice and can always be avoided. Worst case the example method could return mixed values and if the result is not an array it's assumed to be an error message, although there are better alternatives.
Sometimes dependencies are unnecessary (code more naturally belongs elsewhere) or too general (I would question the necessity of injecting high level objects like doctrine or e.g. the container into anything other than test helpers).
If there is a complex dependency to mock (such as on multiple classes from the persistence layer) abstract it out into a wrapper which is far simpler to mock than the complex dependency.

Using IOC Container for multiple concrete types

I want to implement IOC in my application but i am confused, in my application i have multiple concrete classes which implement an interface. Consider this scenario:-
I have an Inteface ICommand and following concrete types which implement this interface:-
AddAddress
AddContact
RemoveAddress
RemoveContact
Basically user performs all this action in UI and then List is passed to the service layer where each command is executed.
So in GUI layer I will write
ICommand command1 = new AddAddress();
ICommand command2 = new RemoveContact();
In command manger
List<ICommand> listOfCommands = List<ICommand>();
listOfCommands.Add(command1);
listOfCommands.Add(command2);
Then finally will pass listOfCommands to service layer.
Now as per my understanding of IOC is only one concrete class is mapped to the interface. And we use this syntax to get our concrete type from StructureMap container.
ICommand command = ObjectFactory.GetInstance<ICommand>();
How should i implement IOC in this scenario?
In this scenario you're better off making your commands into value objects, i.e. not created by the IoC container:
class AddAddressCommand {
public AddAddressCommand(string address) {
Address = address;
}
public string Address { get; private set; }
}
When you create a command, you really do want a specific implementation, and you want to parameterise it precisely, both concerns that will work against the services of the IoC container. This will become even more relevant if you decide at some point to serialize the command objects.
Instead, make the service-layer components that execute the commands into IoC-provided components:
class AddAddressHandler : IHandler<AddAddressCommand> {
public AddAddressHandler(ISomeDependency someDependency) { ... }
public void Handle(AddAddressCommand command) {
// Execution logic using dependencies goes here
}
}
In your case, the component that accepts the list of commands to execute will need to resolve the appropriate handler for each command and dispatch the command object to it.
There's some discussion of how to do this with Windsor here: http://devlicious.com/blogs/krzysztof_kozmic/archive/2010/03/11/advanced-castle-windsor-generic-typed-factories-auto-release-and-more.aspx - the community supporting your IoC container of choice will be able to help you with its configuration.
As mentioned by Mark, StructureMap will allow you to set up and call named instances of an interface:
ObjectFactory.Initialize(x =>
{
x.For<ISomeInterface>().Add<SomeImplementation>().Named("SomeName");
}
You can still add a default instance for that particular interface, of course:
ObjectFactory.Initialize(x =>
{
x.For<ISomeInterface>().Add<DefaultImplementation>();
x.For<ISomeInterface>().Add<SomeImplementation>().Named("SomeName");
}
When you call ObjectFactory.GetInstance<ISomeInterface>(); the default instance (the one initialized with Use instead of Add) is the one that will be returned.
So in your case, the set up would look something like:
ObjectFactory.Initialize(x =>
{
// names are arbitrary
x.For<ICommand>().Add<AddAddress>().Named("AddAddress");
x.For<ICommand>().Add<RemoveContact>().Named("RemoveContact");
}
These would be called as pointed out by Mark:
ObjectFactory.GetNamedInstance<ICommand>("AddAddress");
ObjectFactory.GetNamedInstance<ICommand>("RemoveContact");
Hope this helps.
Most IOC containers allow you to register "named instances" of interfaces, allowing you to register several implementations of ICommand, each with its own unique name. In StructureMap, you request them like this:
ObjectFactory.GetNamedInstance<ICommand>("AddAddress");
Have a look at this question to see how you setup the container in StructureMap.

WCF single vs multiple operations ?. design ideas

We are developing a CRM application. All the business logic and data access go through WCF services. We have 2 options for communication between WCF and client (at the moment: ASP.NET MVC 2)
One option is create method for each operations. Example
GetCustomers()
GetCustomersWithPaging(int take, int skip)
GetCustomersWithFilter(string filter)
GetCustomersWithFilterPaging(string filter, int take, int skip)
or // new .net 4 feature
GetCustomers(string filter = null, int take = 0, int skip = 0)
... list goes..
Another option is create a generic single service operation called
Response InvokeMessage(Messege request). Example
wcfservice.InvokeMessage(
new CustomerRequest {
Filter = "google",
Paging = new Page { take = 20, skip = 5}
});
// Service Implementation.
public Response InvokeMessage(Message request)
{
return request.InvokeMessage();
}
InvokeMessage = generic single service call for all operation.
CustomerRequest = Inherited class from Message abstract class, so I can create multiple classes from Message base class depend on the input requirements.
Here is the CustomerRequest class.
public class CustomerRequest : Message
{
public string Filter {get;set;}
public Page Paging {get;set} // Paging class not shown here.
Public override Response InvokeMessage()
{
// business logic and data access
}
}
EDIT
public abstract class Message
{
public abstract Response InvokeMessage();
}
// all service call will be through InvokeMessage method only, but with different message requests.
Basically I could avoid each service call's and proxy close etc..
One immediate implication with this approach is I cannot use this service as REST
What is the recommended approach if the service needs to call lots of methods?
thanks.
If you use the facade pattern you can have both.
First, build your services using the first option. This allows you to have a REST interface. This can be used externally if required.
You can then create a facade that uses Invoke message style, this translates the request based on the parameters and calls one of the individual services created in the first step.
As to the question of multiple specific operations vs one general query - either approach could be valid; defining fine-grained vs coarse-grained operations is to some degree a matter of taste.
When faced with a similar requirement in our RESTful service, I've chosen to create a filter class that reads parameters from the query-string, available thusly:
public NameValueCollection ReadQuerystring()
{
return WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters;
}
The larger issue that I see here is that you're subclassing Message for your operation parameters - is there a reason why you're doing that? The best practice is to create data contracts (objects annotated with [DataContract] attributes) for such a purpose.

Resources