Správné použití Nette s Kdyby\Events a různé akce
- Ripper
- Člen | 56
Dobrý den,
s Nette začínám a mám pár nejasností. Mám konkrétní příklad s logováním různých interakcí. Narazil jsem na Kdyby\Events a hrozně se mi to zalíbilo a chtěl bych to používat na většině akcích co se na webu dějí, tedy například – změna hesla, přihlášení, odhlášení, atd.
S přihlašováním a odhlašováním jsem to vyřešil celkem jednoduše. Zaregistroval jsem si UserListener a do něj vložil následující –
class UserListener extends Nette\Object implements Kdyby\Events\Subscriber
{
/** @var LogRepository */
protected $logRepository;
/**
* @param LogRepository $logRepository
*/
public function injectLogRepository(LogRepository $logRepository)
{
$this->logRepository = $logRepository;
}
public function getSubscribedEvents()
{
return array(
'Nette\Security\User::onLoggedIn' => 'userLoggedIn',
'Nette\Security\User::onLoggedOut' => 'userLoggedOut'
);
}
public function userLoggedIn(User $user)
{
$this->logRepository->log('Uživatel se přihlásil.', __METHOD__, $user->getId());
}
public function userLoggedOut(User $user)
{
$this->logRepository->log('Uživatel se odhlásil.', __METHOD__, $user->getId());
}
}
A přesně takhle bych to chtěl udělat například když si uživatel změní heslo. Ale jak na to správně? Původně jsem myslel že si prostě vytvořím třídu User která bude dědit od Nette\Object a tam si vložím různé funkce, například funkci changePassowrd a dále tam budu mít proměnou onPasswordChange a na ní napojím UserListener výše. Ale nevím zda je to takto správně a když jsem to zkoušel, tak to akci nezalogovalo.
Děkuji za nakopnutí a omlouvám se za případné nejasnosti v tom co píši. Případně rád dovysvětlím.
- Filip Procházka
- Moderator | 4668
Ripper napsal(a):
Narazil jsem na Kdyby\Events a hrozně se mi to zalíbilo a chtěl bych to používat na většině akcích co se na webu dějí…
Děláš mi radost ;)
A přesně takhle bych to chtěl udělat například když si uživatel změní heslo. Ale jak na to správně? Původně jsem myslel že si prostě vytvořím třídu User která bude dědit od Nette\Object a tam si vložím různé funkce, například funkci changePassowrd a dále tam budu mít proměnou onPasswordChange a na ní napojím UserListener výše. Ale nevím zda je to takto správně a když jsem to zkoušel, tak to akci nezalogovalo.
Kdyby\Events
fungují tak, že aby události fungovaly, musí
třída být registrována v DI Containeru. Není tedy možné dělat události
nad entitami (možné to je, ale není to moc sexy).
Správný postup tedy je, událost vytvořit v nějakém usermodelu.
class RegisteredUsers extends Nette\Object
{
public $onChangePassword = array();
public function changePassword(MyUser $user, $oldPassword, $newPassword)
{
try {
$user->changePassword($oldPassword, $newPassword);
$this->onChangePassword($user);
$this->save($user);
return TRUE;
} catch (InvalidPasswordException $e) {
return FALSE;
}
}
// ...
}
class MyUser extends Nette\Object
{
private $id;
private $password;
public function changePassword($oldPassword, $newPassword)
{
if ($this->password !== my_awesome_crypt_func($oldPassword)) {
throw new InvalidPasswordException("Old password doesn't match");
}
$this->password = my_awesome_crypt_func($newPassword);
}
}
Například takto :) Teď už stačí jen zaregistrovat třídu
RegisteredUsers
jako službu, doplnit chybějící metodu
save
a injekci databázového připojení přes konstruktor a
valíš :)
- dada-amater
- Bronze Partner | 52
Ahoj Filipe,
moc se mi to libi. Narazil jsem ale na problemy s dedenim. Pouzivam
BaseRepository
, ze ktere deni ostatni, napriklad
CommentRepository
. Asi takto:
class BaseRepository extends Nette\Object
{
protected $mapper;
public $onInsert = array();
public $onUpdate = array();
public function update(BaseObject $item)
{
$result = $this->mapper->update($item);
$this->onUpdate($item);
return $result;
}
public function insert(BaseObject $item)
{
$result = $this->mapper->insert($item);
$this->onInsert($item);
return $result;
}
}
BaseObject je predchudce vsech mych zakladnich objektu (entit), napr.
Comment
u.
Pak mam CommentListener
:
class UserListener extends Nette\Object implements Kdyby\Events\Subscriber
{
public function getSubscribedEvents()
{
return array(
'CommentRepository::onInsert' => 'commentInserted'
);
}
public function commentInserted(Comment $comment)
{
//zpracovani
}
}
Problem je, ze commentInserted
se nikdy
nezavola. V DebugBaru pred registrovanim
CommentListener
u mam:
0x BaseRepository::onInsert
no system listeners
Po registrovani CommentListener
u stejne, jen pribyde:
0x CommentRepository::onInsert
Kdyby\Events\Subscriber
Pokud kod nepodedim, ale zapisu ho primo do CommentRepository, tak to taky funguje. Dekuji za radu.
- Filip Procházka
- Moderator | 4668
Když si zapneš panel tak uvidíš jaké eventy jsou registrované a jaké se volají. A z toho jde poznat, že event se vždy volá na classe na které je definovaný. Ve zkratce: je to feature.
Pokud potřebuješ, aby se to volalo zvlášť pro všechny potomky, tak budeš muset ty eventy vyvolávat ručně.
class BaseRepository extends Nette\Object
{
protected $mapper;
/** @var Kdyby\Events\EventManager */
private $evm; // nezapomeň injektovat (konstruktor?)
public function update(BaseObject $item)
{
$result = $this->mapper->update($item);
$this->evm->dispatchEvent(
get_class($this) . '::onUpdate',
new EventArgsList([$this, $item])
);
return $result;
}
public function insert(BaseObject $item)
{
$result = $this->mapper->insert($item);
$this->evm->dispatchEvent(
get_class($this) . '::onInsert',
new EventArgsList([$this, $item])
);
return $result;
}
}
- Tomáš Votruba
- Moderator | 1114
Přidal jsem si úspěšně listener na
Presenter::onStartup
.
Teď bych si chtěl přidat listener do vytváření formuláře. Toto mi ale
nefunguje:
Form.php
/** @var [] */
public $onBuild;
public function attached($presenter)
{
parent::attached($presenter);
$this->onBuild($this);
}
FormListener.php
class FormListener extends Nette\Object implements Kdyby\Events\Subscriber
{
public function getSubscribedEvents()
{
return ['Zenify\Application\UI\Form::onBuild' => 'onBuild'];
// zkoušel jsem i: return ['Zenify\Application\UI\Form::onBuild'];
}
public function onBuild($form)
{
dump($form);
// neproběhne
}
}
Mohu poprosit o nakopnutí?
Editoval Tomáš Votruba (22. 2. 2014 15:33)
- Filip Procházka
- Moderator | 4668
- je služba registována v DIC?
- vidíš event v events debug panelu ?
- co se vygenerovalo do system containeru? vložil se do property Event objekt?
- Tomáš Votruba
- Moderator | 1114
1. V tom byl háček. Používal jsem event v presenteru a ten bylo potřeba jej registrovat jako službu.
Díky.
- sasule
- Člen | 18
Ahoj,
prosím o radu, případné nakopnutí:
Mám vytvořenu událost, která reaguje na vytvoření uživatele, jemuž bych
pak chtěl poslat email, kde bude link na přihlášení/aktivaci nebo cokoli
podobného. A tento link bych chtěl generovat. Email generuji z templatu.
Čili jaký bude lepší přístup? Zapomenout na můj EventListener a volat to na presenteru nebo nějak do EventListeneru dostat (asi) presenter a pomocí něj pak vygenerovat odkaz?
Případně je ještě nějaká jiná možnost, jak odkaz v template vygenerovat?
Osobně se mi moc nelíbí postup níže, protože modelu/fasádě
předávám cosi navíc, než by potřebovala:
Presenter
<?php
...
$this->_users->save($user, $this);
...
?>
Model:
<?php
class Users extends \Sasule\Common\BaseModel {
public $onUserCreated = array();
function save(User $_u, Presenter $_p){
$this->save($_u);
$this->onUserCreated($_u, $_p);
}
?>
Díky předem za případné rady :-)
Editoval sasule (1. 4. 2014 12:13)
- jiri.pudil
- Nette Blogger | 1029
Můžeš použít nějaký jednoduchý generátor URL
Editoval jiri.pudil (1. 4. 2014 12:22)
- Filip Procházka
- Moderator | 4668
No a nebo si nebudeš komplikovat život a vyžádáš si v konstruktoru
toho listeneru třídu Application
, z ní si sáhneš na presenter
->getPresenter()
a link si vygeneruješ.
Případně já to ještě dělám tak, že šablonu presenteru „ukradnu“.
$template = clone $this->app->getPresenter()->getTemplate();
Ten clone je důležitý, aby jsi neupravoval šablonu pro presenter, ale měl vlastní kopii. Můžeš si pak nastavit soubor se šablonou a máš nakonfigurovaný objekt template, ve kterém už budou fungovat makra na odkazy.
- batko
- Člen | 219
Ahoj,
rád bych začal používat Kdyby/Events ale nějak se mi to nedaří rozjet.
- instal přes composer
- config.neon
- i v panelu to mám.
Níže je uveden nástřel servicy která se stará o blog, všechny závisloti jsou tam předány nechcí tady vypisovat kilometorvý kod. Ale nestane se nic.
servica
class BlogService extends \Nette\Object {
public $onInsert = array();
public function save($data) {
$this->onInsert();
//save do DB
}
listener
class FooListener extends Nette\Object implements Kdyby\Events\Subscriber {
public function getSubscribedEvents() {
return array(
'Model\Services\BlogService::onInsert' => 'insert',
);
}
public function insert() {
dump("test");
}
}
- jiri.pudil
- Nette Blogger | 1029
Obě třídy jsou zaregistrované v configu a listener má tag
kdyby.subscriber
?
- batko
- Člen | 219
Ještě malý dotaz. Mám kod uvedený níže. Rád bych kdybych mohl event navěsit na jakoukoliv metodu v service…Ale to co mám nyní mi nefunguje. Jde to nějak?
class BlogService extends \Nette\Object {
public function getAll() {
return $this->blogReposiotry->findAll();
}
a
class FooListener extends Nette\Object implements Kdyby\Events\Subscriber {
/** @var \Nette\Security\User */
public $user;
public function __construct(\Nette\Security\User $u) {
$this->user = $u;
}
public function getSubscribedEvents() {
return array(
'Model\Services\BlogService::getAll' => 'appStartup',
);
}
public function appStartup() {
dump("foo");
}
- batko
- Člen | 219
sasule napsal(a):
Jestli tomu dobře rozumím, tak bych to viděl asi následovně:
<?php class BlogService extends \Nette\Object { public onGetAll = array(); public function getAll() { $this->onGetAll(...); //sem dáš, co předáš do listeneru. return $this->blogReposiotry->findAll(); } ?>
Ok. Takže prostě vždy musím v tém metodě zavolat onGetAll(…) a něco mu předat.
Mě by stačilo kdyby si to vytáhlo Presenter, ale ok beru to jako cenou radu. Děkuji moc.
Editoval batko (7. 4. 2014 13:55)
- David Matějka
- Moderator | 6445
@batko: koukni na kdyby/aop, treba to vyresi tvuj problem. ale nezneuzivej to ;)
- Jiří Nápravník
- Člen | 710
Tak si dovolím tady taky jednu otázku na Kdyby\Events – snad se neztratí:-)
Mám modul pro práci s uživateli, a pak modul, který pracuje s městy. Uživatel může být z nějakého města.
Tak a teď chci smazat město. Ale abych mohl smazat město, chci zjistit nejprve, dza ho nemá nějaký uživatel a případně kolik.
Přijde mi na tohle právě ideální použít Kdyby\Events, na začátku delete metody zavolám událost onPreDeleteCity(). Ale teď nevím, jak to správně nasadit. Napadlo mě jedině, že pokud bude město nasazeno vyhodím si výjimku, která ponese informaci, kolik uživatelů má to dané město a odchytím si to v presenteru a předám do flashMessage.
Ale nevím, je to správně takhle? Nejde to nějak lépe? A je na tohle vůbec dobré použít Kdyby\Events?
- David Matějka
- Moderator | 6445
vyjimku bych na to nepouzival. kdyz uz pouzit eventy, tak bych jako parametr udalosti predal nejaky objekt „Result“ a jednotlive subsribery by tam doplnily potrebne informace
- Jiří Nápravník
- Člen | 710
akadlec: Udělat kontrolu před samotným smazáním a pak kdyžtak smazat, je samozřejmě nejsnazší. Ale je tam už ten problém, že mazání města řeší něco co nemá… Nemluvě o tom, že v budoucnu bude moci být město i další objekty v systému a volat třeba 10 různých „tabulek“ z jednoho místa aby kontrolovali, zda to jde smazat, mi nepřijde příliš košér. A pokud pak použiju jen modul pro města, tak to nepůjde, protože to budu muset zase smazat…
matej21: vyjimka se mi prave taky moc nelibi, je to „ugly“. To s tím resultem zni zajímave, muzes to nejak rozvest? Příliš nechápu… A píšeš „když už použít eventy“, tak se ti to taky příliš nelíbí asi, jak ybs to řešil tedy ty?
Editoval Jiří Nápravník (8. 4. 2014 13:34)
- David Matějka
- Moderator | 6445
jak ybs to řešil tedy ty?
Pokud jde fakt pouze o uzivatele, udelal bych si nejakou sluzbu (treba obecnou fasadu nebo klidne konkretne „CityRemover“), ktera by vyzadovala jak sluzbu pro praci s uzivateli tak pro praci s mesty. Pak by to overilo, zda existujou nejaci uzivatele a smazalo/nesmazalo by se mesto.
Pokud by tech „kolizi“ mohlo nastat vice, udelal bych si nejakou tridu Result, treba takhle
class CollisionResult
{
protected $entries = array();
public function add($type, $collisions)
{
$this->entries[$type] = $collisions;
}
public function getEntries()
{
return $this->entries;
}
}
pak kde bych mazal
class CityManager
{
public function deleteCity($id)
{
$result = new CollisionResult();
$this->onPreDeleteCity($id, $result);
if(count($result->entries)) {
...
} else {
...
}
}
}
ten subscriber na onPreDeleteCity by pak doplnil informace
class DeleteCitySubscriber ....
{
...
public function onPreDeleteCity($id, $collisions)
{
$found = $this->findUsersWithCity($id);
if($found) {
$collision->add('user', $found);
}
}
}
eventy sice asi nejsou to pravy orechovy na tohle, ale je to nejsnadnejsi reseni :)
- Jan Suchánek
- Člen | 404
@Jiří Nápravník: Nebylo by lepší nejprve se pokusit smazat, a až potom volat onErrorEvent?
@matej21: Ten subscriber by vytvářel odpovědi pro flashMessage nebo by se sestavovali až v presenteru?
Editoval jenicek (8. 4. 2014 14:19)
- Jiří Nápravník
- Člen | 710
matej21: díky, tohle vypadá hodně dobre reseni. Mě naopak na tohle prijdou eventy jako delane, jen je tam mozna trochu nezvyk, ze pouziju evente „pred akci“ nez po akci. diky tomu nebudu muset mit uzce svazane moduly. Co se mi tam fakt nelibilo, tak bylo to znasilneni exceptiony jako jsem mel prvne.
jenicek: myslis odchytit si vyjimku, kterou mi vrati databaze a pak to tam teprve resit tim resenim mateje? no neni to spatny napad, ale moc se spoliham na to, ze mi nekdo treba omylem smaze ten foreign key v databazi a pak se mi smaze to co nema:-)
- Jan Suchánek
- Člen | 404
@Jiří Nápravník: No nevim to by pak měli řešit ty vychytaný logy co řešíme jinde.
Mazat FK by ti neměl nikdo jinak jsou k ničemu, pokud tedy nepoužívaš MyISAM.
Jak využiješ ten subscriber následně?
EDIT: měl by se vůbec zobrazit button na smazání, když ta akce není povolená?
Editoval jenicek (8. 4. 2014 15:50)
- akadlec
- Člen | 1326
hmm tak při tomto řešení se mě ty eventy začínají líbit…
Takže všechny tyhle check akce by měly být mimo manager? Dejme tomu že chci
smazat produkt z eshopu, ale nemůžu smazat produkt který byl již někdy
v minulosti koupen, či jej má někdo aktuálně v košíku (košík v db)
takže si udělat preDelete kde se udělá check kolize zda byl někdy produkt
objednán a zda jej někdo nemá v košíku? Oboje pokud to dobře chápu
v DeleteSubsciberu? a pokud to vrátí nějaký počet entries tak provedu
třeba jinou akci?
- Jiří Nápravník
- Člen | 710
jenicek napsal(a):
EDIT: měl by se vůbec zobrazit button na smazání, když ta akce není povolená?
Neměl, ale 1. neni to prilis rozumne resitelne. Muselo by se jedine v databazi ukladat, kolik ma uziti to mesto a kdyz bude nula tak button vykreslit. Nicmene i tak je tam porad ten problem, ze nekdo muze manipulovat z adresou a da to smazat, takze se to sejne musi resit i na urovni kde se to samotne mazani provadi. Taky by na to šli teoreticky udělat eventy, když se přidá uživatel, zavolá se event, který aktualizuje ten counter uziti. Ale tam je pak zase ten nedostatek, že nepůjde vypsat: TOhle město je přiřazeno u 2 uživatelů a čtyř podniků.
akadlec: tak nemusi byt mimo manager. ale dává mi to smysl a je to doržování Single responsibility. Každá třída odpovídá za své. Může to být v jednom subscriberu (v případě shopu to tady asi i dává smysl). Ale třeba u toho mojeho města bude lepší udělat subscriber samostatný v nějakým tom UserModule, a pak třeba další, který bude checkovat jestli není přiřazen třeba u podniku, tak bude v nějakém CompanyModule
- akadlec
- Člen | 1326
Takže dejme tomu že mám 2 moduly – uživatele a podinky a pak jeden modul lokality, tak jak uvádíš. Chci smazat lokalitu a předtím než to udělám zavolám onPreDeleteCity a to mě zavolá dva eventsubscribery, jeden v uživatelích a jeden v podnicích a pokud jeden z nich neco nalezne tak mě to tam vyplní. Pochopil jsem dobře?…toto se mi líbím, aspoň trošku odstraním závislosti, co by se mě kryly do jednoho místa.
- Jan Suchánek
- Člen | 404
@Jiří Nápravník: Ok, myslel jsem nevytvářet button submit a nenavazovat na něj submit, nebo ho nechat disable, pak by to bylo řešitelné i u podvrženého url, ne?
- Jiří Nápravník
- Člen | 710
akadlec: ano takhle to chápu já, a takhel ot nejspíše udělám, protože tím budu moci udělat ty moduly na sobě nezávislé (resp. usermodul bude locationmodule potrebovat porad, ale ne uz obracene)
jenicek: asi jsme se nepochopili, ja to nemam pres nejaky formular, ale mazu ve vypisu, proste vypsano x mest pod sebou a delete jsou klasicke odkazy (chranene tedy secure routu, takze by to podvrzene url bylo stejne tezke odhadnout). No a proto skryvani tech odkazu by bylo casove naroce a neefektivni, protoze by se per odkaz muselo ptat na pocet v tech ostatnich tabulkach. No anebo tedy pouzit ten predpocitany „counter sloupec“
- Jan Suchánek
- Člen | 404
@Jiří Nápravník: Ok už chápu, ja na tohle hlídání používám mazací formulář z CD-collection.
Editoval jenicek (8. 4. 2014 16:55)
- Filip Procházka
- Moderator | 4668
Tak si dovolím tady taky jednu otázku na Kdyby\Events – snad se neztratí:-)
A proč jsi nezaložil vlastní téma? :)
Na použití eventů nevidím nic špatného, jelikož jde o dva různé moduly tak je to možná dokonce ideální.
moc se spoliham na to, ze mi nekdo treba omylem smaze ten foreign key v databazi a pak se mi smaze to co nema:-)
Memyslís že se moc spoléháš na to že budeš mít tabulky v databázi? Nebo že budeš mít kód na hostingu? Tohle je pěkně absurdní obava :)
Přidat si tam vzdálený klíč je výborný nápad který bude fungovat jako poslední záchrana kdybys měl chybu v aplikaci, tak aby si databáze udržela aspoň trochu konzistentní data.
- akadlec
- Člen | 1326
Hele pánové a jak pak vyřešit flash message? V presenteru/komponentě si zavolám $manager->delete($entity) kde se provede zmíněná kontrola zda je to ok nebo ne, ale co pak dělat když to ok není? Zmíněná výjimka byla označena jako nevhodná, takže co?
- Filip Procházka
- Moderator | 4668
Vyhazovat výjimky z eventů rozhodně nechceš. Tedy bych do toho eventu
klidně předal nějaký validation objekt, na který půjdou přidávat errory.
Podobně jako má formulář isValid
a addError
.
- Jan Suchánek
- Člen | 404
@akadlec: K těm zprávičkám, už jsem to zmínil, nemohl by se o to starat ten Subscriber?
@Filip Procházka: Ten validaton objekt by mohl být service, tedy předán z neonu?
Editoval jenicek (8. 4. 2014 19:00)
- Filip Procházka
- Moderator | 4668
Rozhodně ne service, protože pro každé zavolání potřebuješ novou instanci.
- Jiří Nápravník
- Člen | 710
Filip Procházka napsal(a):
A proč jsi nezaložil vlastní téma? :)
Sem se to celkem hodí a je to takové „support vlákno“ Kdyby\Events na foru:-)
Memyslís že se moc spoléháš na to že budeš mít tabulky v databázi? Nebo že budeš mít kód na hostingu? Tohle je pěkně absurdní obava :)
Přidat si tam vzdálený klíč je výborný nápad který bude fungovat jako poslední záchrana kdybys měl chybu v aplikaci, tak aby si databáze udržela aspoň trochu konzistentní data.
já jsem trochu paranoik no:-D ten cizi klic tam bude, protoze to jede na doctrine, ale furt si rikam, co kdyz prijde nejaky novy matlal, ktery zacne hrabat primo v databazi, ale ok nebudu paranoidni:-)
@jenicek neon ne, videl bych to na nově vytvoreny objekt pres new, ktery pošleš do subscriberu a vytáhneš si co to vratilo.
- akadlec
- Člen | 1326
@Filip Procházka: nn nemyslel sem vyhazovat výjimky z eventů ale přímo z manageru
class CityManager
{
public function deleteCity($id)
{
$result = new CollisionResult();
$this->onPreDeleteCity($id, $result);
if(count($result->entries)) {
throw new SomeExceptionName('Nemuzes mazat protoze XY');
} else {
$this->dao->delete($entity);
}
}
}
class CityPresenter
{
handleDelete($id)
{
try {
$this->manager->deleteCity($id);
} catch (SomeExceptionName $e) {
$this->flashMessage('Nemohl sem to smazat protoze Xy');
} catch (NextSomeExceptionName $e) {
$this->flashMessage('Nejaky jiny duvod proc to a ono');
}
}
}
Editoval akadlec (8. 4. 2014 19:29)
- Filip Procházka
- Moderator | 4668
Sem se to celkem hodí a je to takové „support vlákno“ Kdyby\Events na foru:-)
Nemám rád support vlákna, v momentě kdy mají víc než dvě stránky je v tom pak bordel ;)
akadlec napsal(a):
@Filip Procházka: nn nemyslel sem vyhazovat výjimky z eventů ale přímo z manageru
Tak to pak jo, na tom není vůbec nic špatného. Já to taky tak dělám.
- Jiří Nápravník
- Člen | 710
Btw jak moc ošklivé je, když si budoucí flasmessage hodím, do exception? Zkrátka řekněme ten příklad, že nemůžu smazat město, protože ho mají nastavené dva uživatelé. Jak to pak předat nejlépe uživateli?
- vrátím si objekt, který bude mít informaci o počtu těch uživatelů vytáhnu a nastavím flashmessage
- vyhodím výjimku, v které bude message Nelze smazat, město mají dva uživatelé. Odchytnu vyjímku a message nastavím na flashMessage
Někde jsem četl, že správně by se exception message neměli přehazovat k uživateli.
Nebo ještě nějak lépe?
- Filip Procházka
- Moderator | 4668
Rozhodně špatně, to nechceš. Ale co by šlo tak třeba tohle;
use Nette\Reflection\ClassType;
trait RenderableException
{
public $count;
public $parameters;
public $domain = 'front';
public function renderMessage(Kdyby\Translation\Translator $translator)
{
$message = ClassType::from($this)->getShortName() . '.' . $this->code;
return $this->translator->translate($message, $this->count, $this->parameters, $this->domain);
}
}
Nadefinuješ si výjimku
class CityRemovalException extends \RuntimeException
{
const RELATION_RESTRICTION = 1;
const LOCKED = 2;
// ...
use RenderableException;
public function __construct($message, $code = 0, \Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
$this->domain = 'city';
}
public static function relationRestriction(City $city)
{
return new static("Some relation is bound to " . $city->name . " and it cannot be deleted", self::RELATION_RESTRICTION);
}
// ...
}
vyhodíš
public function delete($city)
{
if ($this->isReferenced($city)) {
throw CityRemovalException::relationRestriction($city);
}
}
chytneš a informuješ
try {
$cityManager->remove($city);
$this->flashMessage("ok");
} catch (CityRemovalException $e) {
$this->flashMessage($e->renderMessage($this->getTranslator()));
}
Je to jenom koncept, musíš si to doladit :)