j7mbo / aurex
aurex is a merge between the Silex micro-framework and the awesome Auryn dependency injector.
Requires
- php: >=5.5.9
- dflydev/doctrine-orm-service-provider: 2.0.0
- monolog/monolog: 1.13.1
- rdlowrey/auryn: 1.0.1
- silex/silex: 2.0.x-dev#c207787
- symfony/doctrine-bridge: 2.7.*@dev
- symfony/form: 2.7.*@dev
- symfony/security: 2.7.*@dev
- symfony/translation: 2.7.*@dev
- symfony/twig-bridge: 2.7.*@dev
- twig/twig: 1.18.0
- useallfive/doctrine-data-fixtures-command: dev-master
Requires (Dev)
- phpunit/phpunit: 4.5.1
This package is not auto-updated.
Last update: 2024-11-09 17:50:51 UTC
README
Silex on steroids
Aurex is a merge between the Silex micro-framework and the awesome Auryn dependency injector.
Aurex is useful for rapid application development with best practice in mind. If you know what you're doing with code, you'll love it.
It comes with the following already integrated:
- Doctrine ORM with Entities / Repositories / Command Line / Fixtures
- Symfony Forms that work with Doctrine entities
- Monolog logging, YAML config parsing and caching
- Working custom user provider, user entity and login form
- Twig Templating Engine integration
- Working firewall - currently, anonymous users can visit
/
and/login
. Everything else is secured! - Controllers as services (each controller is an object with action methods)
- Environment specific configuration files
- "Modules" to group individual service provider integration and setup
- Recursive auto-wiring dependency injection in every object from your controllers onward
If you know how to use Silex then you know how to use Aurex, the only differences being a different controller resolver to instantiate controllers and their dependencies recursively and service providers being setup within individual objects (modules).
You can use this as a standalone framework with which to write good, SOLID code, without using a service locator with which to pull in dependencies, or fork it to see how to get Silex working with the latest symfony components. Effectively, this setup allows you to typehint for any object, anywhere, and it will automatically be resolved for you, allowing you to focus on what you're creating and not how they are passed to eachother.
Aurex is setup with the latest Symfony 2.7 components, uses Silex 2, and will be maintained to stay up-to-date with Silex releases.
Example
We're going to go through the process of creating a page that displays some data retrieved via an API call. This involves:
- Creating a route
- Creating it's corresponding controller and template
- Using a third party library to make the API request in our controller
- Displaying the data retrieved from the API call in our template
This is a very rudimentary example but the point is to show how easy it is to get an object where you need in the application by dependency injecting it without requiring any additional configuration / object registration.
Add a route in Application/Config/routes.yml
:
hello: pattern: /hello controller: HelloController:indexAction template: hello.html.twig
Create the controller as: Application/Controller/HelloController
with the indexAction
method. Extend AbstractController
if you want access to commonly used objects like security, the request, session or entity manager.
namespace Aurex\Application\Controller; class HelloController extends AbstractController { public function indexAction() { /** -- SNIP **/ } }
Decide that you want to use the Guzzle Http library, for example, so require it with composer:
require guzzlehttp/guzzle:~5.0
Typehint for GuzzleHttp\Client in your controller to have it automatically passed in for you with no extra configuration (thanks, Auryn Injector). Then return the data to the template:
public function indexAction(GuzzleHttp\Client $client) { /** @var $json A json string from the httpbin.org containing the origin ip address **/ $json = $client->get('http://httpbin.org/ip'); $data = json_decode($json, true); $origin = $data['origin']; return [ 'origin' => $origin ]; }
Create your template as web/templates/hello.html.twig
:
<p> Hello, you have access to the $origin variable in this Twig template. <br /> It's value is: {{ origin }}. <p>
Visit http://aurex.local/hello
to see your page.
This is basically the same as Silex / Symfony, yet it's much more streamlined and faster. But what's the point of this? Any object you need to access, like Guzzle's Http Client, you don't need to set up. You can just typehint for it in any of your controllers and it's passed in for you. This is also recursive - so if one object needs another object, typehint only for the first one and the dependency will also be created so that the first one can be injected. This is a really powerful tool to enable you to rapidly develop applications without worrying about how to gain access to the objects you need.
This is a very basic example. For other examples, including aliasing and more, see EXAMPLE.md (currently a WIP).
Installation
-
You need composer installed
-
Run
composer create-project j7mbo/aurex ./ 0.2.1
-
Create a virtual host (if using apache, else check the silex web servers documentation ):
<VirtualHost aurex.local:80> ServerName aurex.local ServerAlias aurex.local DocumentRoot "/path/to/aurex/web/" DirectoryIndex index.php <Directory "/path/to/aurex/web/"> Options Indexes MultiViews FollowSymlinks Order allow,deny Require all granted AllowOverride All Allow from All </Directory> </VirtualHost>
-
Set up your log file with permissions for both your user (for doctrine cli) and your webserver (for the application). If all else fails,
chmod 777
it and feel bad about your life -
Create a database with the settings in lib/Application/Config/dev.yml (feel free to change)
-
Create the database schema with
vendor/bin/doctrine orm:schema-tool:create
-
Run the fixtures to load the user roles and user into the database with:
vendor/bin/doctrine fixtures:load ./lib/Application/Model/Fixture --append
-
Login with
admin@aurex.com
andpassword
- you can see how the fixture data is loaded from lib/Application/Model/Fixture/LoadUsers.php -
Start writing awesome code in lib/Application as this isn't version controlled
Auryn Dependency Injector
Auryn is a recursive dependency injector. It uses reflection,
and subsequently caches those reflections, to read constructor and method signatures of objects and builds them in
reverse-order for you. An object requiring multiple objects as dependencies can be created with $injector->make('Object');
instead of calling new Object(new Object2(new Object3))
etc.
The injector calls make()
and execute()
on controller constructors and methods so devs can typehint for any object
they require in a controller and it will automatically be instantiated and passed in for them when the controller code
is executed. This does away with the service locator anti-pattern
and allows you to write SOLID, object-oriented code without worrying about how to wire your objects together or
instantiate them in your controllers.
The following methods available to the auryn injector are used:
- $injector->alias() - Maps from interfaces or abstract classes to concrete implementations
- $injector->share() - Shares a single instance around the application removing the need for a singleton
- $injector->delegate() - To delegate an object's instantiation to a factory
The Auryn settings are located in lib/Application/Config/dev.yml under the auryn_module
key.
I highly recommend checking out the Auryn repository and playing around with it to see how it works.
The result is that you can typehint for any object, abstract or interface in your controller and it will be passed in for you!
Modules
The integration of individual service provider objects with their relevant configurations should be handled (wrapped) by a single object, or a "module", to keep each separate. Previously, one large procedural file was used to setup all the service providers. Now service provider usage can adhere more to SRP.
The ModuleLoader
loads any objects implementing ModuleInterface
. Each Module is passed the Aurex
application object
which contains the configuration and auryn injector objects, which you can then use to set up your individual service provider.
Modules are loaded in the order that lib/Application/Config/global.yml displays them. To load a custom module you have just created, add the fully qualified class name (including namespace) and the loader will load the module provided in the specified order.
Use modules to integrate other service providers or other objects that will act on the Silex\Application
object. Each
module can also interact with the Auryn injector to share, delegate or perform whatever other functionality is required
to make your object work throughout the rest of the application.
For an example of a module, see the RoutingModule. This file is responsible for setting up routes according to the (already parsed) routes.yml file.
Bootstrapping
In the event you need to do any custom application bootstrapping after the Aurex one has executed, you
can modify the lib/Application/index.php, as this file is included by the former before
Silex\Application::run()
is called.
Templates
Twig templates are located in web/templates. If you specify a template
key as seen in routes.yml
,
your controller only needs to return an array of data and it will be passed to the template. If you omit the template
key, you can typehint for \Twig_Environment
in your controller and call ::render()
with the relevant parameters to
render your template. The former method is purely for convenience.
To access the user object within templates, Silex provides `{{ app['security.token_storage'].token.user }}.
Environments
Usually Applications require different environment setups like "dev" and "live". Aurex comes with a default DevEnvironment which sets some xdebug settings if available. The environment is built by an EnvironmentFactory.
You can create your own custom environment by implementing EnvironmentInterface
and running whatever environment-specific
logic you require in the perform(Aurex $aurex)
method. The file global.yml
contains settings to load your own
environment files.
Config Caching
The annoying thing about configuration parsing (especially with YAML) is that, on every request, the data is read from
the files before the application is booted. As a result there is an I/O constraint and this data should be cached if at
all possible. If you have memcached installed, the ParserCacherFactory
stores parsed configuration data in memcached
to remove the I/O overhead.
As a result, if you add / remove configuration variables from any configuration files and you have a caching implementation in effect, (currently restricted to memcached), you will need to restart memcached or else manually remove the configuration keys (currently the configuration file paths) to have the new data used instead.