10 ottobre 2014, Symfony Day, Milano
Matteo Galli
Software Engineer @ Facile.it (Utilities)
...ma anche fotografo e viaggiatore
www.facile.it
matteo.galli@facile.it
twitter.com/thinkindie
instagram.com/thinkindie
Facile.it è un comparatore di tariffe
I nostri uffici sono a Milano, ma soprattutto...
WE ARE HIRING!
Siamo presenti anche su GitHub github.com/facile-it
Bunny
+
=
<3
WebService RESTful basato sull'esistenza di risorse (ad es. le entità di Doctrine) di cui si ha accesso alle loro rappresentazioni tramite un identificatore globale (URI).
GET /api/users/1 HTTP/1.1
Host: ws.bunny.facile.it
Accept: application/vnd.api+json
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
date: Thu, 19 Oct 2014 14:38:16 GMT
{
"id":1,
"timestamp":"2014-06-10T15:42:37+02:00",
"lastUpdateTimestamp":"2014-06-10T15:42:37+02:00",
"username":"BunnyWs",
"nome":"Bunny",
"cognome":"WS",
"email":"bunnyws@facile.it",
"isActive":true
}
Semplifica e velocizza lo sviluppo di un WebService RESTful
/**
* @RouteResource("Compagnia")
*/
class CompagnieController extends BunnyController
{
/**
* @View()
*/
public function cgetAction() {}
/**
* @View()
*/
public function getAction(Request $request) {}
/**
* @View()
*/
public function postAction(Request $request) {}
/**
* @View()
*/
public function patchAction(Request $request) {}
/**
* @View()
*/
public function deleteAction(Request $request) {}
}
SmartReindexing: la modifica di una entità richiede l'aggiornamento di tutti i documenti in cui è contenuta
$inheritanceTree = $this->getInheritanceTree($rootEntity);
$entities = $this->em->getConfiguration()->getMetadataDriverImpl()->getAllClassNames();
foreach ($entities as $entity) {
$class = $this->cmf->getMetadataFor($entity);
$associationMappings = $class->getAssociationMappings();
if (empty($associationMappings)) {
continue;
} else {
foreach ($associationMappings as $nestedEntity) {
if (in_array($nestedEntity['targetEntity'], $inheritanceTree)) {
...
}
}
}
}
DoctrineMetadata: ho già una mappa (ricorsiva) delle relazioni tra entità
ACL = Access Control List
Permettono di definire regole (ACE) capillari per accedere a risorse.
Es.: permessi dei files su piattaforme Unix, regole del firewall.
La gestione base delle ACL viene affidata al provider di default di Symfony e vengono persistite su MySQL
Le ACE vengono definite in ciascuna entità tramite custom annotations
namespace Facile\Ws\BunnyBundle\Entity;
use Facile\Ws\BunnyBundle\Annotation\EntityAce as ACE;
/**
* @ACE("ROLE", name="ROLE_ADMIN", mask="MASK_MASTER");
* @ACE("ROLE", name="ROLE_USER", mask="MASK_VIEW");
*/
class Compagnia
{
/**
* @ACE("ROLE", name="ROLE_USER", mask="MASK_VIEW")
* @ACE("ROLE", name="ROLE_ADMIN", mask="MASK_MASTER")
*/
protected $nome;
}
(Un comando si occupa poi di inizializzare tutte le entità)
Utilizzare il provider di default di Symfony per filtrare una collezione di entità non è performante né soddisfacente.
(Non provatelo a casa)
You know, for search!
Quando una entità viene salvata su ElasticSearch il documento viene diviso in due parti
{
"data" : {
"lastUpdateTimestamp" : "2014-10-08T09:28:45+02:00",
"id" : 5,
"timestamp" : "2014-06-10T15:42:33+02:00",
"venduto" : true,
"nome" : "Facile.it",
"type" : "energia"
},
"meta" : {
"acl" : [
{
"mask" : 1,
"name" : "role_user",
"type" : "role"
},
{
"name" : "role_admin",
"mask" : 64,
"type" : "role"
}
]
}
}
La ricerca di documenti viene eseguita con \Elastica\Query e filtrata con i ruoli ricoperti dall'utente attivo
$elasticaFilterOr = new \Elastica\Filter\BoolOr();
foreach ($this->getUserExpandedRoles() as $role) {
$elasticaFilterRolesName = new \Elastica\Filter\Term();
$elasticaFilterRolesName->setTerm('meta.acl.name', strtolower($role));
$elasticaFilterRolesType = new \Elastica\Filter\Term();
$elasticaFilterRolesType->setTerm('meta.acl.type', 'role');
$elasticaFilterAndRole = new \Elastica\Filter\BoolAnd();
$elasticaFilterAndRole->addFilter($elasticaFilterRolesName);
$elasticaFilterAndRole->addFilter($elasticaFilterRolesType);
$elasticaFilterOr->addFilter($elasticaFilterAndRole);
}
$elasticaQuery = new \Elastica\Query(new \Elastica\Query\MatchAll());
$elasticaQuery->setFilter($elasticaFilterOr);
Utilizzando i filtri (dalla documentazione di ES):
Redis è un database di tipo chiave-valore, dove la chiave, come il valore, possono essere una qualsiasi sequenza di bytes.
http://redis.io
Redis è utilizzabile per:
Bunny utilizza Redis per la cache di
doctrine:
orm:
entity_managers:
bunny:
metadata_cache_driver:
type: redis
host: localhost
port: 6380
query_cache_driver:
type: redis
host: localhost
port: 6380
result_cache_driver:
type: redis
host: localhost
port: 6380
Redis viene utilizzato anche per la gestione di code con Resque tramite php-resque.
Qualsiasi operazione che particolarmente complessa viene demandata quindi ad un processo asincrono o programmato.
Esempi di processi asincroni:
https://joind.in/talk/view/12215