Binding repositories to routes - binding

I'm using the Repository pattern in a way very close to the one Chris Fidao's used on Implementing Laravel book. Basically I have concrete repository classes that implements its interfaces and gets models injected.
Now I want to take advantage of Laravel's route binding. Since I'm using repositories, I can't bind them to models directly... right? However I didn't get to do this.
I'm using a service provider to bind my concrete repositories to interfaces, like that:
$app->bind('App\Repositories\UserInterface', function ($app) {
return new EloquentUser(new User);
});
How can I bind my routes to repositories interfaces? Seems to be trivial, but I'm a bit lost...
Thank you in advance! :)

You can use a different approach to pass a model to a form without binding a model to a route, for example, assume you have a route which uses UserController and this is the controller:
class UserController extends BaseController {
public function __construct(UserInterface $UserRepo)
{
$this->repo = $UserRepo;
}
public function edit($id)
{
$user = $this->user->find($id);
return View::make('user.edit')->with('user', $user);
}
}
Your form in the user.edit view:
{{ Form::model($user, array('route' => array('user.update', $user->id))) }}

You can use a Repo in your routes the following way. But, do you really need this?
\\the repo is in the Branches folder
use App\Repos\Branches\BranchRepository;
Route::get('branches',function(BranchRepository $branchRepo){
\\using a method of your repo - in this case using getData() method
return $branchRepo->getData();
});

Related

Laravel 4: how to inject another class in a eloquent model

I'm trying to use the built-in laravel's Ioc container to inject a PageManager class inside a Page model and I'm a little lost.
What I'm trying to achieve is something like that:
class Pages extends Eloquent {
public function __construct(PagesManagerInterface $manager, array $attributes = array())
{
parent::__construct($attributes);
$this->manager = new $manager;
}
public function saveToDisk()
{
$this->manager->writeToFile();
}
But I obtain this error:
ErrorException: Argument 1 passed to Pages::__construct() must be an instance of PagesManagerInterface, none given.
I tried to add this in app/start/global.php:
App::bind('Pages',function(){
return new Pages(new PagesManager);
});
But is seems ignored by the framework, and also i don't know how to insert the $attribute array into this declaration.
I'm a little lost so any help is appreciated!
It's not a good idea to overload a model's constructor because new instances can be spawned behind the scenes through various methods, like Model::find().
When that happens, the dependencies you're asking for in your custom constructor aren't being passed in because the Model class isn't aware of them. So, you get that error message.
See the find() method here: http://laravel.com/api/source-class-Illuminate.Database.Eloquent.Model.html#380-397
See this post by Jason Lewis: http://forums.laravel.io/viewtopic.php?pid=47124#p47124
I think that what you need is:
App::bind('PagesManagerInterface',function(){
return new Pages(new PagesManager);
});
This tells Laravel to inject a new Page object everytime it needs an instance of your PagesManagerInterface wich wasn't passed while creating the model.
In Laravel you can use the IoC Container:
public function saveToDisk(){
$managerObject = app()->make('path\to\class\PagesManagerInterface');
$managerObject->writeToFile();
}

Looking for a good practice for dealing with repository in asp.net mvc

Currently I've got repository class for each data model for data access.
In the homepage, I need to display the top 5 records from each repository. So in the HomeController, I would need to have a lot of repository members, which doesn't feel right. Is there any good solution for this?
Thank you very much for your help. Any advice is welcome!
You could use a service layer which will aggregate those five repositories into a single business operation. Example:
public interface IItemsService
{
IEnumerable<Item> GetTopItems();
}
and the implementation of this service will use as much repositories as required. Now your controller simply becomes:
public class ItemsController : Controller
{
private readonly IItemsService _itemsService;
public ItemsController(IItemsService itemsService)
{
_itemsService = itemsService;
}
public ActionResult Index()
{
var topItems = _itemsService.GetTopItems();
return View(topItems);
}
}
And in the view:
#model IEnumerable<AppName.Models.Item>
<h2>Top items</h2>
#Html.DisplayForModel()
which uses the item display template (~/Views/Items/DisplayTemplates/Item.cshtml):
#model AppName.Models.Item
<div>#Model.Title</div>
you could do a generic Repository for basic operations, and inherit it when creating repositories for each entity in part.
but this way you would still need 5 instances or the generic repository.
so there are 2 ways:
1)
create a repository UniRepo
which has Generic Methods like Get<T>(int id), etc.
and use it in your HomeController
2)
don't put any instance of repository in HomeController, instead
create 5 controllers for each entity, and in each you put an instance of a repository, and create an action for that list
and in the home/index view you do
#Html.Action("list","entity1");
#Html.Action("list","entity2");
#Html.Action("list","entity3");
Html.Action is equivalent to RenderAction
I second Darin Dimitrov's response.
In my experience it is usual (in desktop development) to have a service layer that does all the talking to your repositories.
e.g.
View Model
Service
Repository1 : Repository 2
Data Access 1 : Data Access 2
then from the top end your only ever calling your service(s) which keeps things neet.

Extending sfDoctrineRecord in symfony

I've added some functionality to some of my instance classes in my symfony project that I want ALL of my instance classes to have. If I didn't have any qualms about editing the core symfony installation, I would just add my methods directly to the sfDoctrineRecord class. But I don't want to do that, of course, because my changes would break on upgrade, plus my changes wouldn't port well to other projects.
If I want to add certain functionality to all my instance classes in symfony, what's the "right" way to do that?
(P.S. When I say "instance class", I mean something like lib/model/doctrine/Customer.class.php.)
Steps:
Create myDoctrineRecord
abstract class myDoctrineRecord extends sfDoctrineRecord
{
public function commonRecordMethod() { }
}
I place this file in lib/record, but you can put it anywhere that the autoloader will see it.
Set Symfony to use this class in the configureDoctrine callback of your ProjectConfiguration:
public function configureDoctrine(Doctrine_Manager $manager)
{
sfConfig::set('doctrine_model_builder_options', array('baseClassName' => 'myDoctrineRecord'));
}
That's it! Isn't Symfony great? :)
I suppose the proper way would probably be to add a Doctrine_Template to the models in question, however you would need to define it as a behavior for every model in your schema.yml
class MyMethodsTemplate extends Doctrine_Template
{
public function customMethod1(){
$model = $this->getInvoker();
//do stuff with model
}
public function customMethod2(){
$model = $this->getInvoker();
//do stuff with model
}
}
And then in your schema.yml:
ModelName:
actAs:
MyMethodTemplate: ~
# the rest of your definition
After you rebuild you should be able to call:
$model = new ModelName();
$model->customMethod1();
$model->customMethod2();
Of course Doctrine templates and listeners are much more powerful than that. You should take a look at the documentation for decent overview

How to perform conditional binding in StructureMap from within a registry and without the use of generics?

I am familiar with Ninject and in Ninject you can do something similar to
Bind<ICalendar>().To<MonthCalendar>().WhenInjectedInto(typeof(Roster)).InRequestScope();
I'm not sure how to perform something similar in StructureMap. I need to be able to do this dynamically from my own binding without the use of the generic StructureMap methods. Additionally, I need to be able to do it from inside a Registry class. For example...
For(binding.Source).LifecycleIs(GetLifecycle(binding.Scope)).Use(binding.Destination);
I have looked at stack overflow and codebetter for ideas but can not work out how to do conditional binding as in the aforementioned Ninject example.
If I interpret your Ninject configuration correctly - the generic solution in structure map would be:
For<Roster>().HttpContextScoped().Use<Roster>()
.Ctor<ICalendar>().Is<MonthCalendar>();
Edit:
For doing the same thing with fully dynamic registrations instead you should be able to use this:
For(binding.Source).LifecycleIs(binding.Lifecycle)
.Use(binding.Destination).Child(binding.ChildSource)
.IsConcreteType(binding.ChildDestination);
For configuring a type registration dynamically you could use a convention:
public class DynamicConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
TypeRegistrationSettings typeSettings = FindTypeSettings(type);
if (typeSettings == null)
return;
registry.For(typeSettings.Source)
.LifecycleIs(typeSettings.Lifecycle).Use(typeSettings.Destination);
}
}
Where FindTypeSettings(type) would look up your own bindings.
The convention is registered with a scan:
ObjectFactory.Initialize(
c =>
c.Scan(s => {
s.TheCallingAssembly();
s.Convention<DynamicConvention>();
})
);

Inject different repository depending on a querystring / derive controller and inject the repository depending on the controller type / ASP.NET MVC

I have a search form that can search in different provider.
I started out by having a base controller
public SearchController : Controller
{
protected readonly ISearchService _searchService
public SearchController(ISearchService searchService)
{
_searchService= searchService;
}
public ActionResult Search(...)
{
// Use searchService to query and return a view.
}
}
And child controllers
TwitterController : SearchController
{
...
}
NewsController : SearchController
{
...
}
I use StructureMap to insert all my dependencies in the controller. With this setup, I was able to change the SearchService depending on the type of the controller being instanciated.
x.For<ISearchService>().ConditionallyUse(o =>
{
o.TheDefault.Is.OfConcreteType<NewsSearchService>();
o.If(c => c.ParentType == typeof(TwitterController))
.ThenIt.Is.OfConcreteType<TwitterSearchService>();
...
});
That even allowed me to set different Views for each controller, (just putting the corresponding folder (Twitter, News...) and the Parent controller is still handling all the Search, with a simple
return View(results)
which is displaying the correct view specific to twitter, news, or other
Now that was cool and looked great, I a single form and the different views are displayed in tabs on the same page. That's where it starts to get complicated with this approach. The form has to post to /Twitter to search in twitter, to /News to search in news... which means I should change the action parameter of the form depending on which tab I am and display the correct tab on when the form returns depending on.. the url? craziness follows.
If you have built something like this already or know what's the best approach to this, please advices are welcome.
Now I think I would have less pain using a parameter in the form and posting to a single controller. I am thinking of injecting the correct SearchService depending on this parameter. What would be the best approach? I thought of using a model binder,
So I would have my ActionMethod that look like this:
public ActionResult Search(ISearchService service, Query query)
{
var results = service.Find(query);
}
But I think would need to make a call like this in the ModelBinder
ObjectFactory.GetInstance(...);
Based on the querystring parameter that describe which provider to use, and that doesn't seem more elegant to me. I feel stuck, help :(.
Whenever you need to vary a dependency based on a run-time value, Abstract Factory is the general solution.
Instead of injecting ISearchService into your Controllers, inject an ISearchServiceFactory:
public SearchController : Controller
{
private readonly ISearchServiceFactory searchServiceFactory;
public SearchController(ISearchServiceFactory searchServiceFactory)
{
if (searchServiceFactory == null)
{
throw new ArgumentNullException("searchServiceFactory");
}
this.searchServiceFactory = searchServiceFactory;
}
public ActionResult Search(...)
{
// Use searchServiceFactory to create an ISearchService based on
// run-time values, and use it to query and return a view.
}
}
It is not entirely clear to me which run-time value you need to vary on, but assuming that it's the Query, ISearchServiceFactory might be defined like this:
public interface ISearchServiceFactory
{
ISearchService Create(Query query);
}
I was trying to figure out how to use the abstract factory pattern and still let structuremap resolve all the dependencies of my components.
I believe that is the way I am going to implement it, but I submit this here to get some feedback if someone would read this.
As explain in the previous answer, I do not want to build the whole object graph depending on which provider I need in the Abstract factory.
ie :
class StatServiceFactory : IStatServiceFactory
{
public IStatService Create(string provider)
{
switch(provider)
{
case "blog":
return new StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
//How to resolve the Config, the SearchManager, and BooleanQueryBuilder?
//Add more abstract factories? It starts to get messy in my opinion...
}
}
}
What I can do is have the abstract factory use my container to create an instance of my search managers depending on a parameter (coming from the querystring in my case)
Structuremap allows to create named instances this way :
x.For<ISearchManager>().Use<AbcSearchManager>().Named("Abc");
x.For<ISearchManager>().Use<DefSearchManager>().Named("Def");
I need a way to inject the container in my Abstract factory.
I would probably wrap the container in a wrapper defined like this. That would keep me from leaking Structuremap into my project. I dont need more that those 2 features within the abstract factory anyway, but it is not necessary:
public interface IContainerWrapper
{
object GetInstance<T>();
object GetNamedInstance<T>(string key);
}
and the implementation :
public class ContainerImpl : IContainerWrapper
{
private readonly Container _container
public ContainerImpl(Container container)
{
_container = container;
}
...
}
And setup StructureMap to resolve dependencies to my abstract factory like that :
x.For<IContainer>.Use(new ContainerImpl(this));
x.For<IFactory>.Use<Factory>()
My factory would be then much simpler and would create my instance like that :
public class SearchmanagerFactory
{
private readonly IContainerWrapper _container;
public SearchmanagerFactory(IContainerProvider containerProvider)
{
_container = containerProvider;
}
public ISearchManager Create(string provider)
{
//eed to handle the bad input for provider.
return (ISearchManager)
_container.Resolve<ISearchManager>(provider);
}
}
That seems pretty clean this way :).
Thoughts?
This is more an extensive comment than an answer to explain why an AbstractFactory seems complicated. Here is something that looks closer from the reality:
class StatServiceFactory : IStatServiceFactory
{
public IStatService Create(string provider)
{
switch(provider)
{
case "blog":
return new StatService(IFacetRepository,ISearchManager,IConfigManager,BooleanQueryBuilder);
//How to resolve the Config, the SearchManager, and BooleanQueryBuilder?
//Add more abstract factories? It starts to get messy in my opinion...
}
}
}
The FacetRepository is the same for any provider, but the SearchManager changes, the ConfigManager changes, and the BooleanQueryBuilder is an abstract class with different implementation for different provider (because every API doesnt use the same keyword for their queries) All those dependencies are currently resolved by structuremap, based on the type of the controller.
I would really like to keep the benefit of StructureMap here, rather than using factories all the way, for each different pieces.'
Please see my edit at the end of my question for another suggestion to my problem.

Resources