문제

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.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top