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

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.

Related

Domain Mapping - Convert Models from external Software to internal structure

i'm currently working on a php-framework to abstract and make it fun and easy to work with an external software ($ES) my company consults. My approach is the hexagonal design pattern, which works great so far. My only concern is mapping (and where to map) entities from $ES to our internal structure.
Example:
$externalSoftwareProduct (kind of a god class which handles everything)
is mapped to $internalFrameworkProduct (and many other classes to split responsibilities). This happens in Repositories. In every repository method, i collect those entities from $ES and do
new $internalFrameworkProduct(some arguments here coming from
$externalSoftwareProduct)
foreach of my collected entities which then gets returned. In those repositories there are only generic methods, like getById, getAll, you name it.
Now we use this framework in a customer project and extend those base classes with domain specific extension, like CustomerNameProductRepository.
There you find domain specific methods like getProductsCustomerAlwaysNeeds and so on. At the end of those methods, we map the $internalFrameworkProduct to $customerSpecificProduct which holds data for easier access, which is needed. A method in this specific repository looks like this.
public function getProductsCustomerAlwaysNeeds()
{
$dataStuff = parent::getSomeStuff();
/** #var internalFrameworkProduct[] $products **/
$products = magic();
foreach($products as $product)
{
$customerProducts[] = $this->getCustomerSpecificProduct($product->getId());
}
return $customerProducts;
}
public function getCustomerSpecificProductById(int $productId)
{
$externalSoftwareProduct = new externalSoftwareProduct($productId)
$customerSpecificProduct = new CustomerSpecificProduct(some arguments here coming from $externalSoftwareProduct)
return $customerSpecificProduct;
}
Now this works fine so far. The only problem is in unit tests. We are using phpunit + Mockery. In order to mock those new created instances we have to use mock(overload:externalSoftwareProduct) and mock(overload: CustomerSpecificProduct) which is always a pain (especially if you try to test this with multiple instances, which is needed from time to time).
How would you approach this? There must be a better way to get those 3 Pieces connected (externalSoftwareProduct, internalFrameworkProduct and CustomerSpecificProduct (which extends internalFrameworkProduct)).
I was thinking about using a factory for the CustomerSpecificProduct in order to just mock the factory and let it spit out my Products. But i feel like im overengineering such a simple task.
«.... mapping (and where to map) entities from $ES to our internal structure...»
In the adapter you use to access the external software.

How to structure Laravel applications when not using Eloquent ORM

I'm new to Laravel and am working on a collection of API endpoints that fetch data from a variety of database tables, transforms and processes that data, and then returns it as a JSON response. What I'm struggling to decide, from reading the documentation, is where my processing/transformation code should live.
I've setup my routes in routes/api.php, and have them point towards a Controller subclass, but from here things get a little murky because I'm not currently looking to make use of Eloquent ORM. It seems like typically I'd generate Model subclasses for each database table, and have a Repository call into those and transform the returned data, like so:
Route => Controller => Repository => Model
But where should I be placing both the database query code, and the logic required to process/transform that data (the business logic), when not making use of Eloquent ORM or the Model paradigm? Making the controller fat with DB queries and logic seems like a messy solution, but at the same time a lot of the Laravel DB example code does place logic in Controller subclasses.
So I'll offer an approach that is I think accomplishes what you are asking. In the API I developed, I used the route/api.php file to create the API endpoints. Each of these point to a Controller where the authentication and request validation is performed. The Controller then calls a Service class that handles all the business logic of the application and these are the real heavy lifting parts. The Service class makes calls to a Repository, which actually performs Model changes and saving.
I saw this used in another project several years ago and mimicked it for my projects. The code flow is shown below. Not sure if it will work for you, but I find it to be a very neat and keeps the code grouped together in logical ways.
Route => Controller => Service => Repository => Model
Here is an example of how you can setup your project without Eloquent.
Models are just data containers (records). They don't know how or where to store themselves. They are instantiated with some data and provide access for it:
class OrderRecord
{
protected $id;
protected $createdAt;
public function __construct($id, $createdAt = null)
{
$this->id = $id;
$this->createdAt = $createdAt ?: date('d-m-Y H:i:s');
}
public function getID()
{
return $this->id;
}
...
}
You read and write models only via repositories. Repository is just a class with your typical methods to retrieve/write one or multiple records (find, findAll, update, delete etc). It reads data, instantiates record(s) with it or take record(s), gets data from them and writes it. For example:
class OrderRepository
{
public function find($id): OrderRecord
{
// Here goes your querying. StdClass will be returned in this case
// that will be used to create new record.
$row = DB::table('orders')->find($id);
if ($row) {
return (new OrderRecord($row->id, $row->created_at));
} else {
// If not found you can throw an exception or return null
// (return type should be ?OrderRecord then) depending on
// how you want to design your workflow with repositories.
...
}
}
...
}
Lastly your business logic can be in your controllers. Your controllers should use repositories to get your model objects, work with them and the use repositories to store result if needed.
Obviously, you will need to implement base classes/interfaces for all your entities. Use dependency injection and Laravel's service container to glue everything together.
You can also extend this design by separating querying and mapping in your repository - you will have Repository classes that know only how to retrieve/store raw data and you will have Mapper classes that know how to create/retrieve raw data to/from your records (and serve as factories that repository will use to generate record objects from query results). If business logic also need to be interchangeable then you can also take business logic out of your controllers and put it into separate classes that your controller can utilize.
Here is an excellent article about repository-based approach and how you can extend it.

Configurable dynamic Doctrine database entities in Symfony 2

What I am trying to achieve
Users would be able to configure Doctrine entities through an HTML form on a website.
Users would be able to define new entities, as well as add and delete fields for existing entities. (Similar to Drupal's content types)
The Doctrine entities would get dynamic properties based on the configuration that the user supplied through the web UI.
Either the single DB table per Doctrine entity would be altered dynamically whenever an entity configuration changes; Or there could be multiple tables used per single entity (each new entity field would get its own table).
Done so far
I have been researching this for the past few days without much success but I stumbled across this answer which seems quite related to what I am trying to achieve.
I have registered and added the loadClassMetadata listener which maps the field foo:
// src/DynamicMappingTest/AdminBundle/EventListener/MappingListener.php
namespace DynamicMappingTest\AdminBundle\EventListener;
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
class MappingListener
{
public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
{
$classMetadata = $eventArgs->getClassMetadata();
if ($classMetadata->getName() != 'DynamicMappingTest\\AdminBundle\\Entity\\CustomNode')
{
// Not the CustomNode test class. Do not alter the class metadata.
return;
}
$table = $classMetadata->table;
$oldName = $table['name']; // ... or $classMetaData->getTableName()
// your logic here ...
$table['name'] = 'custom_node';
$classMetadata->setPrimaryTable($table);
$reflClass = $classMetadata->getReflectionClass();
dump($reflClass);
// ... or add a field-mapping like this
$fieldMapping = array(
'fieldName' => 'foo',
'type' => 'string',
'length' => 255
);
$classMetadata->mapField($fieldMapping);
}
}
Now, this all works as long as I have the foo property declared in the DynamicMappingTest\AdminBundle\Entity\CustomNode class:
// src/DynamicMappingTest/AdminBundle/Entity/CustomNode.php
namespace DynamicMappingTest\AdminBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* CustomNode
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="DynamicMappingTest\AdminBundle\Entity\CustomNodeRepository")
*/
class CustomNode
{
...
private $foo;
}
Problem
However, there is no way for me to know what properties the users will define for their custom entities. If I remove the foo property from the CustomNode class, the ReflectionClass that I get from the ClassMetadata will naturally not include the foo property and so I get the following exception whenever the mapField() in MappingListener is executed:
ReflectionException: Property DynamicMappingTest\AdminBundle\Entity\CustomNode::$foo does not exist
in vendor/doctrine/common/lib/Doctrine/Common/Persistence/Mapping/RuntimeReflectionService.php at line 80
77. */
78. public function getAccessibleProperty($class, $property)
79. {
80. $reflectionProperty = new ReflectionProperty($class, $property);
81.
82. if ($reflectionProperty->isPublic()) {
83. $reflectionProperty = new RuntimePublicReflectionProperty($class, $property);
Questions
Is it possible to have fully configurable dynamic Doctrine entities?
Am I on the right track with my approach? If not, could you suggest an alternative?
How could I have truly dynamic class properties? Or should I be generating new Doctrine entity PHP classes whenever the users change the entity configuration?
Is it possible to have fully configurable dynamic Doctrine entities?
Doctrine generates proxy classes for you entities. That means that doctrine generates PHP code with class, which extends your Entity class and overrides the methods - puts some custom logic and then calls the parent method.
So, I think that the only way to make this really happen is to generate the PHP code for entities in your code. That is, every time entity is created in your website, you should generate PHP file with that entity, then run migrations.
Am I on the right track with my approach? If not, could you suggest an alternative?
I don't think that you should use Doctrine ORM at all in this case, at least in the way you're trying to do that.
Generally, ORM is used for easier/more manageable programming. That is, you can set relations, use lazy-loading, unit of work (change entity properties and then just flush) etc. If your entities are generated dynamically, what features will you use at all? Developer will not write code for these entities, because, as you've said, there is no way to know what fields it will have.
You haven't provided concrete use-case - why do you want to do that in the first place. But I imagine that it could be really done in some easier way.
If users can store any structure at all, should you use MySQL at all? ElasticSearch or similar solutions could be really much better in such cases.
How could I have truly dynamic class properties? Or should I be generating new Doctrine entity PHP classes whenever the users change the entity configuration?
As I've mentioned - yes. Unless you would want to override or replace some of Doctrine code, but I imagine it could be lots of it (proxy classes etc.)

Autoload a simple DAO in Symfony2+Propel

I've been working on a project and I decided it'd be a good idea to have some sort of, like, DAO, but simplified.
Basically, the only thing I want from it (right now, at least) is to fetch me objects by model name and id. I wrote this very simple piece of code:
class DAO {
public static function get($className,$id) {
$queryName = $className."Query";
if (!class_exists($className) || !class_exists($queryName)) {
return false;
}
$q = $queryName::create()->filterByID($id)->find();
return $q;
}
}
However, I found myself stuck with the implementation. I guess I need to somehow autoload it so that it'll be able to check for the existence of the classes and so that I could use it anywhere inside my app, but I don't know how. Can anyone help me out? Or if there's a better way to do that, I'll appreciate any input.
What you're looking for is a Service.
Definition from the documentation:
Put simply, a Service is any PHP object that performs some sort of
"global" task. It's a purposefully-generic name used in computer
science to describe an object that's created for a specific purpose
(e.g. delivering emails). Each service is used throughout your
application whenever you need the specific functionality it provides.
Defining your class as a service is as simple as this:
app/config/config.yml
...
services:
my_dao:
class: Your\Bundle\DAO
...
Now you can access DAO in your controllers doing something like this:
$dao = $this->get('my_dao');
When you make this call, the Service Container will create an instance of your class and return it. There will always be at most one instance (singleton) and if it's never called, it won't even be instantiated.
I recommend reading the documentation.
Opinion
It seems like you're having trouble adapting to the Symfony way.
If you take a look at The Book you'll see that the Entity Manager in conjunction with your entity's Repository handle most of what DAO's traditionally did. In other words, there's really no need for your DAO class.
For example, fetching any object by id is as easy as:
$om->getRepository('YourBundle:YourModel')->find($id);
Anyway, if you're particularly fond of that approach, you may want to try this project.

PHP workaround to extend classes of the same name?

I know extending a class with the same name is not possible, but I was curious if anyone knew of a way to load a class then rename it, so i can later extend it with the original name. Hopefully like something below:
<?php
//function to load and rename Class1 to Class2: does something like this exist?
load_and_rename_class('Class1', 'Class2');
//now i can extend the renamed class and use the original name:
class Class1 extends Class2{
}
?>
EDIT:
Well, I understand that this would be terrible practice in a basic OOP environment where there are large libraries of class files. But i'm using the CakePHP MVC framework and it would make great sense to be able to extend plugin classes in this way since the framework follows a well established naming convention (Model names, view names, controller names, url routes (http://site.com/users), etc).
As of now, to extend a CakePHP plugin (eg: Users plugin) you have to extend all the model, view, and controller classes each with different names by adding a prefix (like AppUsers) then do some more coding to rename the variable names, then you have to code the renamed url routes, etc. etc. to ultimately get back to a 'Users' name convention.
Since the MVC framework code is well organized it would easily make sense in the code if something like the above is able to be implemented.
I'm trying to work out why this would be necessary. I can only think of the following example:
In a context that you have no control over, an object is initialised:
// A class you can't change
class ImmutableClass {
private function __construct() {
$this->myObject = new AnotherImmutableClass();
}
}
$immutable = new ImmutableClass();
// And now you want to call a custom, currently non existing method on myObject
// Because for some reason you need the context that this instance provides
$immutable->myObject->yourCustomMethod();
And so now you want to add methods to AnotherImmutableClass without editing either Immutable class.
This is absolutely impossible.
All you can do from that context is to wrap that object in a decorator, or run a helper function, passing the object.
// Helper function
doSomethingToMyObject($immutable->myObject);
// Or decorator method
$myDecoratedObject = new objectDecorator($immutable->myObject);
$myDecoratedObject->doSomethingToMyObject();
Sorry if I got the wrong end of the stick.
For more information on decorators see this question:
how to implement a decorator in PHP?.
I happen to understand why you would want to do this, and have come up with a way to accomplish what the end goal is. For everyone else, this is an example of what the author may be dealing with...
Through out a CakePHP application you may have references to helper classes (as an example > $this->Form->input();)
Then at some point you may want to add something to that input() function, but still use the Form class name, because it is through out your application. At the same time though you don't want to rewrite the entire Form class, and instead just update small pieces of it. So given that requirement, the way to accomplish it is this...
You do have to copy the existing class out of the Cake core, but you do NOT make any changes to it, and then when ever you upgrade cake you simply make an exact copy to this new directory. (For example copy lib/Cake/View/Helper/FormHelper.php to app/View/Helper/CakeFormHelper.php)
You can then add a new file called app/View/Helper/FormHelper.php and have that FormHelper extend CakeFormHelper, ie.
App::uses('CakeFormHelper', 'View/Helper');
FormHelper extends CakeFormHelper {
// over write the individual pieces of the class here
}

Categories