In questi giorni mi sono chiesto se il catpcha fosse veramente la soluzione al problema Bot per SocialCibus. Così mi sono letto diversi articoli in rete. Alcuni di questi parlavano di un 3-10% di utenti che, quando vedono un captcha, lasciano perdere la registrazione, altri abbandonano solo dopo aver sbagliato una o due volte l’inserimento del testo.
Posso tenere fuori i bot e dentro gli esseri umani senza captcha? E’ la domanda che si sono posti in tanti.
Continue reading
Category Archives: Symfony
Notifiche con Doctrine: ereditarietà con relazioni diverse per i figli
Stavo cercando di creare un sistema di notifiche (tipo wall di FB) di ciò che accade agli utenti del mio nuovo sito di ricette e mi sono imbattuto in un problema: ereditarietà con Doctrine e relazioni diverse per ogni figlio.
Premetto che il sito è realizzato con Sf 1.4 e Doctrine 1.2.
Accantonato per il momento l’utilizzo di database documentali e varianti, rimanendo nel mondo del relazionale, per l’implementazione del sistema di notifiche ho fatto il seguente ragionamento:
ci sono principalmente due modi per memorizzare le informazioni su “cosa sta accadendo” (es. nuova ricetta di un amico, commento ad una mia ricetta, follow, like, etc.):
-
Leggere ogni volta tutti gli inserimenti dalle rispettive tabelle (Recipe, Comment, Follow, Like)
- Pro: non devo aggiungere modelli al db
- Contro: la lettura delle informazioni è lentisssssima (devo leggere tutte le tabelle), la computazione (merge e ordinamento) è impegnativa e aumenta al crescere delle informazioni memorizzate
- Scrivere e leggere su una tabella che raggruppa le notifiche.
- Pro: il fetch è velocissimo, posso mantenere la distinzione delle tipologie di notifica, non è necessaria computazione aggiuntiva oltre al fetch per merge e ordinamento
- Contro: devo realizzare e gestire un repository per le notifiche, la mappatura ai vari tipi di notifica non è banale
Naturalmente ho scelto la seconda, che sicuramente è la soluzione più pulita e performante.
Di seguito lo schema (semplificato) dei modelli da “notificare”.
id:
user_id:
title:
...
relations:
User: { class: sfGuardUser, ... }
Comment:
id:
user_id:
recipe_id:
comment:
...
relations:
User: { class: sfGuardUser, ... }
Recipe: { class: Recipe, ... }
Follow:
user_id:
followed_user_id:
relations:
User: { class: sfGuardUser, ... }
FollowedUser: { class: sfGuardUser, ... }
Like:
id:
user_id:
recipe_id:
...
relations:
User: { class: sfGuardUser, ... }
Recipe: { class: Recipe, ... }
Ed eccoci a modellare le notifice: per realizzare la mappatura alle varie tipologie di notifica ho deciso di utilizzare l’ereditarietà con aggregazione di colonne di Doctrine.
columns:
user_id:
dest_user_id:
subject_model:
subject_id:
...
relations:
User: { class: sfGuardUser ... }
DestUser: { class: sfGuardUser, ... }
indexes:
subject_id_index:
fields: [subject_id]
NotificationComment:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 1 }
NotificationRecipe:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 2 }
NotificationFollow:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 3 }
NotificationLike:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 4 }
Bene, ora ho quello che mi serve… o quasi. Considerando che subject_id sia l’id del soggetto della notifica (es. Ricetta o Commento) mi serve una relazione con il repository del soggetto stesso, cosicchè possa avere direttamente l’oggetto relativo alla notifica:
$recipe = $notification_recipe->getRecipe();
Come aggiungo le relazioni? Ho provato con le relazioni “classiche”:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 1 }
relations:
Comment: { class: Comment, ... }
NotificationRecipe:
inheritance: { extends: Notification, type: column_aggregation, keyField: type, keyValue: 2 }
relations:
Recipe: { class: Recipe, ... }
Funziona! Ma… il problema è che alla creazione del database le relazioni le crea… TUTTE nella stessa tabella, quindi abbiamo Notification che ha come constraint tutte le relazioni definite, con Recipe, Comment, Like e Follow.
Il che non funziona affatto, poichè ad ogni inserimento avremo errori di chiavi importate inesistenti nella tabella Notification. Sigh!
Ricominciamo ed eliminiamo le relazioni dallo schema.yml.
Cercando in rete ho trovato un attributo che fa al caso mio: Doctrine::EXPORT_CONSTRAINTS
Quindi ho modificato i modelli dicendo di non esportare le constraint:
{
public function setUp()
{
parent::setUp();
$this->hasOne('Recipe', array(
'local' => 'subject_id',
'foreign' => 'id',
'onDelete' => 'cascade'));
$this->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL ^ Doctrine::EXPORT_CONSTRAINTS);
}
}
Si può fare anche direttamente nello schema.yml ma non ho indagato otre, perchè già così… FUNZIONA!!!
Buon divertimento
Doctrine migrate: quando ci si accorge di essere in ritardo
A volte può capitare di essere pronti per il deploy di una applicazione quando ci accorgiamo di aver dimenticato di creare gli script per aggiornare il database in produzione.
Con Symfony, Doctrine e un sistema di versioning è possibile rimediare molto facilmente.
Questa è la mia ricetta:
- Recuperare la versione di
config/doctrine/schema.yml
che rappresenta il database in produzione (verosimilmente il file che è adesso in produzione
oppure con il sistema di versioning) - Lanciare
./symfony doctrine:build --all
- Copiare l’attuale file
config/doctrine/schema.yml
in un posto sicuro
- Sostituire il file
config/doctrine/schema.yml
con la copia “vecchia” recuperata
- Lanciare
./symfony doctrine:generate-migrations-diff
(se necessario con il parametro env settato)
- Aprire il file
*version*.php
appena creato dalla procedura in
lib/migration/doctrine(se è la prima volta che migri ce n’è uno solo altrimenti apri solo l’ultimo)
- Inverti il nome dei metodi “up()” e “down()”, cioè sostituisci il nome “up()” con “down()” e viceversa
- Riporta lo
schema.yml
all’ultima versione
- Ora puoi lanciare il task di migrazione
./symfony doctrine:migrate
- Nel caso di errori di CHARSET o COLLATION nel lanciare il task precedente, verifica che nel file di migrazione che hai modificato precedentemente non siano presenti i parametri ‘charset’ e ‘collate’ come opzioni dei metodi createTable()
Buona migrazione a tutti.
Symfony: caricare helper dalle action
Talvolta è utile poter caricare alcuni helper dal controller o dal modello, per esempio mi è capitato di dover formattare una data o utilizzare alcuni dei miei helper direttamente dal controller.
Per far questo è sufficiente inserire il codice seguente nel metodo in cui si ha la necessità:
sfLoad::helpers(array('helper1', 'helper2', ...));
Buon coding.
UPDATE: per sf 1.4
$this->getContext()->getConfiguration()->loadHelpers('Partial');
$this->getContext()->getConfiguration()->loadHelpers(array('helper1', 'helper2', ...));
Symfony e Ajax, finalmente il “cart” anche con jQuery
Ho appena patchato il plugin sfJqueryReloaded (il trunk) per abilitare il supporto a jq_drop_receiving_element(), ultimo tassello mancante per poter riscrivere il tutorial ajax di symfony anche con jQuery.
Potete trovare la demo riscritta in http://cart.demo.keasoft.it/ e la patch (in attesa di approvazione dai lead del plugin) qui jQueryHelper.php.diff
Buon divertimento
Far convivere Symfony 1.0, 1.1 e 1.2
A volte capita di dover far convivere sulla stessa macchina di sviluppo diverse versioni si Symfony. Ci sono diverse soluzioni per poter realizzare il nostro scopo e qui vi esporrò quella che utilizzo più frequentemente.
Presupposto che la maggior parte delle applicazioni siano scritte per SF 1.0 oppure che questo sia già installato via PEAR e presupposto che vogliamo installare anche la 1.1 e la 1.2 sulla stessa macchina, procediamo come segue:
- installazione di SF 1.0 via PEAR
- installazione di SF 1.1 e 1.2 via SVN
L’installazione classica di SF da manuale via PEAR è la seguente (la versione corrente è la 1.0.18):
$ pear channel-discover pear.symfony-project.com
$ pear install symfony/symfony-1.0.18
In questo modo quando lanciamo il comando
$ symfony -V
otterremo la versione appena installata
symfony version 1.0.18
Ora procediamo con l’installazione di Symfony 1.1 da SVN: prendendo spunto dal manuale prendiamo la versione corrente, non quella di sviluppo ma quella stabile (ad oggi la 1.1.3).
Prepariamo la directory che ci servirà come deposito di SF 1.1 e 1.2
$ mkdir ~/symfony_rep
$ cd ~/symfony_rep
Ora prendiamo l’ultima release del ramo 1.1
$ svn co http://svn.symfony-project.com/tags/RELEASE_1_1_3
$ ln -s RELEASE_1_1_3 1.1
Facciamo la stessa cosa per la 1.2 (se ne avete bisogno) ma in questo caso non essendo ancora stata rilasciata la stabile dovremmo prendere la versione di sviluppo come da manuale
$ svn co http://svn.symfony-project.com/branches/1.2
Ottimo, abbiamo tutte e tre le versioni installate, ora non ci resta che aggiungere 2 alias per rendere l’utilizzo della versione 1.1 e 1.2 più semplice.
$ alias sf11='~/symfony_rep/1.1/data/bin/symfony'
$ alias sf12='~/symfony_rep/1.2/data/bin/symfony'
Questi alias per comodità potete inserirli nel vostro ~/.bashrc così che siano sempre disponibili ad ogni riavvio (o apertura di nuove shell).
Se tutto è andato bene, digitando i vari sf11 e sf12 doveste ottenere il seguente risultato:
$ sf11 -V
symfony version 1.1.3 (/home/VOSTROUTENTE/symfony_rep/RELEASE_1_1_3/lib)
$ sf12 -V
symfony version 1.2.0-DEV (/home/VOSTROUTENTE/symfony_rep/1.2/lib)
Buon lavoro!



