Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Laravel 4 models in custom classes / subsystems without class dependency

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 attachedenter image description here

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:

enter image description here

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!

like image 632
deb0rian Avatar asked Jan 09 '14 17:01

deb0rian


2 Answers

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']);
       //...
    }

    //....
}
like image 98
Ayobami Opeyemi Avatar answered Oct 15 '22 15:10

Ayobami Opeyemi


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.

like image 40
Andreas Avatar answered Oct 15 '22 16:10

Andreas