Say I have an interface CrawlerInterface
with implementation PageCrawler
and FeedCrawler
; if we happen to need both classes in a controller, how can that be achieved with constructor injection?
Previously we use a central ServiceProvider
to register (i.e. App::bind
) such classes, but in most cases we only have 1 implementation of an interface, so said problem hasn't occured to us yet.
PS: I also wonder if this problem suggests we should split the controller.
Updates:
Thanks for the comments and response, to explain, said interface has only one public method: crawl($uri)
, and both page/feed crawler implements it as given a resource identifier, return resource.
My follow up question:
Say we are in a calculator
scenario where Addition, Subtraction and Multiplication share the same interface Operation
, which has only 1 public method run
, at some point we will still encounter this problem right? How do we handle situation like these in general with ServiceProvider
?
If each crawler exists for a different reason, you can use arbitrary names for your instances, for example:
App::bind('crawler.allArticles', 'PageCrawler');
App::bind('crawler.latestArticles', 'FeedCrawler');
For the controller:
App::bind('CrawlerController', function($app) {
return new CrawlerController(
App::make('crawler.allArticles'),
App::make('crawler.latestArticles')
);
});
Your controller code would then use each crawler differently:
public function showLatestArticlesAction()
$latestArticles = $this->latestArticlesCrawler->crawl();
// ...
}
public function showAllArticlesAction()
$allArticles = $this->allArticlesCrawler->crawl();
// ...
}
If you just have a list of crawlers where each is used for the same thing, you probably want to do something like:
App::bind('crawlers', function($app) {
return [
App::make('PageCrawler'),
App::make('FeedCrawler'),
];
});
In your controller, you'll get a list of "crawlers" by configuring it like so:
App::bind('CrawlerController', function($app) {
return new CrawlerController(App::make('crawlers'));
});
Your controller code could be something like this:
public function showArticlesAction()
$allArticles = array();
foreach ($this->crawlers as $crawler) {
$allArticles = array_merge($allArticles, $this->crawler->crawl());
}
// ...
}
Ok lets assume you have a CrawlerController
class CrawlerController extends BaseController
{
protected $crawler1;
protected $crawler2;
public function __construct(CrawlerInterface $c1, CrawlerInterface $c2)
{
$this->crawler1 = $c1;
$this->crawler2 = $c2;
}
}
an interface
interface CrawlerInterface{}
and concrete implementations of that intefrace called PageCrawler
and FeedCrawler
class PageCrawler implements CrawlerInterface{}
class FeedCrawler implements CrawlerInterface{}
You would inject the dependencies by writing a service locator like
App::bind('CrawlerController', function($app) {
$controller = new CrawlerController(
new PageCrawler,
new FeedCrawler
);
return $controller;
});
But as suggested by others you should rethink your logic, use it only if this kind of architecture is unavoidable
I think that the interface won't help you in this case.
By doing:
App::bind('CrawlerInterface', '<implementation>');
You need to choose one:
App::bind('CrawlerInterface', 'PageCrawler');
or
App::bind('CrawlerInterface', 'FeedCrawler');
And then Laravel will inject it:
class CrawlerController {
public function __construct(CrawlerInterface $crawler)
{
}
}
To have both you have 2 options
-Have 2 different interfaces
-Inject the implementations directly:
class CrawlerController {
public function __construct(PageCrawler $pageCrawler, FeedCrawler $feedCrawler)
{
}
}
But I also think that, if you need something like this, you better rethink your logic.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With