I'm building a parser system that will parse loads of different XML/JSON feeds upon request/cronjob. I use Laravel 4.
The purpose of the thread is to use IoC in my context, and not hardcoded Model names in custom-class methods
Providing an example of parser for Soccer Player with XML structure like:
<players category="Midfielders">
<player id="777">
<name>Caio Augusto Paim do Santos</name>
<statistic>
<club name="Camaçari" id="7191" league="Baiano 2" league_id="1136" season="2013" minutes="" appearences="" lineups="" substitute_in="" substitute_out="" substitutes_on_bench="" goals="" yellowcards="" yellowred="" redcards=""/>
I've created an additional directory in my /app folder called /parsers These are custom classes, they all extend or implement custom abstracts/interfaces in the same folder and basically are responsible for receiving path to XML/JSON file and returning a well-structured PHP arrays.
They are added in composer.json in autoload as: "app/parsers"
Screenshot of file structure attached
All is good and the code/classes are testable and not dependent on another classes, but here's the problem.
Checkout the XML example, there's thing like:
<club id="XXX" league_id="YYY" />
this is feed club id and feed league id, but I have my own IDs in database referenced to feed IDs.
Like on this screenshot:
So the logic says: Go to database, check if there's id in league league table with feed_id provided from XML file. If yes, get it, if not, create a new league and get the id for future references.
This requires me to use Model in my parser classes, now I know you can use IoC and inject models into Controllers, but I'm not sure I can do the same with my parser classes...
So doing something like this in the middle of my parser class:
// Try to get league and season ids from database if they already exists, if not, insert
$leagueId = DB::select('SELECT id FROM league WHERE feed_id=?', array($data['league_id']));
or
$league = new LeagueModel();
Is pretty much incorrect.
Now just to clarify the way it all works, my parsers are getting called in Laravel Command classes like this:
/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$this->setParser();
$this->setStorage();
$this->parser->parseFile($file);
}
And Laravel Command classes are getting called in my Controllers like:
$stamps = $this->getStamp();
Artisan::call('command:getSoccerPlayer',array('stamps' => $stamps, 'parser_id' => Request::segment(2)));
The Controller itself is called via URI:
/jobs/soccer_player/parse?type=soccer&directory=players
**What do you suggest or how would you overcome this issue to avoid dependencies and still use Models for interactions with the database in this context? **
P.S Please don't pay attention that the whole parse logic on my screenshot is in the same method "parse" now, I will break it into pieces once I see the full picture of how I want it to work/look.
Appreciate any help!
you can still call your namespaced models
use App\Models\League;
class SoccerPlayerParser extends AbstractParser{
//...
public function parse()
{
//...
$league = App\Models\League::find($data['league_id']);
//...
}
//....
}
I see two possible solutions here, but am not 100% sure how to integrate it in your project.
The first is to store the name of the model class to use in a config file and intantiate the model via new $class
where $class is a value retrieved via Config::get
or similar. This solution is very common in packages, and even Laravel itself uses it (see the model setting in app/config/auth.php).
The other is to not instantiate the model, but instead create an interface for it and then dependency-inject it into your command. You can easily auto-inject stuff into your commands by using Artisan::resolve('MyNamespace\MyCommand')
instead of Artisan::add(new MyCommand)
, and then inject via type hinting as you do via controllers. http://laravel.com/docs/ioc#practical-usage
Once you've set up the interface as an argument to the command's constructor, you can use App::bind('MyInterface', 'MyModel')
to tell Laravel which class to inject, and this can be swapped at any point.
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