DOCTRINE2 – Self-referencing v AclRole
- Joacim
- Člen | 229
Zdravím, trošku jsem se v tom zamotal ZDE Doctrine2 Návod.
Mám tabulku acl_role, kde jsou všechny role a rodiče těchto rolí, přičemž role nemusí mít potomka(nebo může mít potomků více) a potomek má vždy jen jednoho rodiče (jako je tomu u dědění rolí v Nette).
CREATE TABLE IF NOT EXISTS `acl_role` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(45) NOT NULL,
`parent_id` INT(11) NULL,
PRIMARY KEY (`id`, `parent_id`),
UNIQUE INDEX `name_UNIQUE` (`name` ASC),
INDEX `fk_acl_roles_acl_roles1_idx` (`parent_id` ASC),
CONSTRAINT `fk_acl_roles_acl_roles1`
FOREIGN KEY (`parent_id`)
REFERENCES `acl_role` (`id`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci
COMMENT = 'List of all roles'
A Entitu pro AclRole
<?php
namespace App\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Doctrine entity for table AclRole.
* @package App\Model\Entities
* @ORM\Entity
* @ORM\Table(name="acl_role")
*
* @author
*
* Rodič má 0 až více potomků (1:0..N)
*/
class AclRole extends \Kdyby\Doctrine\Entities\BaseEntity {
/**
* Sloupec pro ID role.
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*
* @ORM\ManyToOne(targetEntity="AclRole", inversedBy="ac_children")
* @ORM\JoinColumn(name="id", referencedColumnName="parent_id")
*/
protected $id;
/**
* Sloupec pro jméno role.
* @ORM\Column(type="string")
*/
protected $name;
/* ------------------------- Association Mapping ------------------------ */
/**
* Sloupec pro ID rodiče (PARENT)
* @ORM\Column(name="parent_id", type="integer")
*/
protected $parent;
/**
* Všichni potomci současné role (CHILDRENS)
* Namapovaná vazba role 1:N na seznam všech potomků.
* @ORM\OneToMany(targetEntity="AclRole", mappedBy="parent_id")
*/
protected $ac_children;
/* --------------------------- Entity Methods --------------------------- */
public function __construct() {
parent::__construct();
$this->ac_children = new ArrayCollection();
}
public function AddChildren(AclRole $children) {
$this->ac_children[] = $children;
$children->user = $this;
}
}
// Prevzato jako ukázka z https://www.zdrojak.cz/clanky/ukladame-hierarchicka-data-v-databazi-i/
ID PARENT TITLE
1 NULL Jídlo
2 1 Ovoce
3 2 Červené
4 2 Žluté
5 3 Třešeň
6 4 Banán
7 1 Maso
8 7 Hovězí
9 7 Kuřecí
- Jen se chci ujistit, že je tato entita a její reference na sebe samu napsaná dobře. A zda se mám u aktuální role dotazovat na jejího potomka nebo rodiče ?
- Další věc co by mě zajímala jak řešíte pro ROLE jako je admin zdroje Permission::ALL (Vložíte do db ke zdroji NULL) IAuthorizator?
EDIT
Už to asi mám, ale neodzkoušeno, asi jsem nad tím přemýšlel déle než
bylo zdravé.
Každopádně objekt AclRole by měl uchovávat aktuální entitu (ID 1) a poté všechny její potomky v ArrayCollection( tedy parent entity je roven 1(rodici, který je uložen v promenné children)), ale v příkladu to ukládají do atributu children, neměl by být u entity array collection ac_children kde bude pole všech potomků a v children bude uložen jejich rodič a v parent rodič children ? – Možná mi jen něco uniká
/** @Entity */
class AclRole
{
// ...
/**
* @OneToMany(targetEntity="AclRole", mappedBy="parent")
*/
private $children; // ID
/**
* @ManyToOne(targetEntity="AclRole", inversedBy="children")
* @JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
// ...
public function __construct() {
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
}
Díky moc za pomoc
Editoval Joacim (2. 2. 2016 20:04)
- Joacim
- Člen | 229
F.Vesely napsal(a):
Proc potrebujes znat v acl children?
Spíš potřebuji znát rodiče, jen nevím jak to napsat, po tom co jsem se do toho zamotal, prohledal jsem i stackowerflow a další fora, ale tento vztah se nejspíše zas tak často nepoužívá, našel jsem jen xml zápis.
Potom existuje ještě možnost že si na to napíši metodu ve fasádě(což bych musel stejně, jen jsem to nechtěl prasit).
Dle DB schématu je toto základní entita, u které chybí selfreference
<?php
namespace App\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Doctrine entity for table AclRole.
* @package App\Model\Entities
* @ORM\Entity
* @ORM\Table(name="acl_role")
*
* @author
*
* Rodič(ID) má 0 až více potomků(ID) (1:0..N)
* Rodič potomka(ID) je uložen v parent_id u potomka
*/
class AclRole extends \Kdyby\Doctrine\Entities\BaseEntity {
/**
* Sloupec pro ID role.
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*
*/
protected $id;
/**
* Sloupec pro jméno role.
* @ORM\Column(type="string")
*/
protected $name;
/**
* Sloupec pro ID rodiče (PARENT)
* @ORM\Column(name="parent_id", type="integer")
*/
protected $parent;
/* ------------------------- Association Mapping ------------------------ */
/**
*
* Namapovaná vazba role 1:N na seznam všech uživatelů.
* Jedna role má N uživatelů.
*
* @ORM\OneToMany(targetEntity="User", mappedBy="role")
*/
protected $ac_user;
/* --------------------------- Entity Methods --------------------------- */
public function __construct() {
parent::__construct();
$this->ac_user = new ArrayCollection();
}
public function addUser(User $user) {
$this->ac_user[] = $user;
$user->role = $this;
}
}
Editoval Joacim (3. 2. 2016 8:14)
- akadlec
- Člen | 1326
Doctrine stránky znáš? Konkrétně self-referencing tam zmíněný je.
OT: asi bych se raději vyhnul extendovat BaseEntitu z Kdyby
- Joacim
- Člen | 229
akadlec napsal(a):
Doctrine stránky znáš? Konkrétně self-referencing tam zmíněný je.
OT: asi bych se raději vyhnul extendovat BaseEntitu z Kdyby
Mám stažený článek z IT Network, tam to takhle má(zda je to dobře nedokážu posoudit s Doctrine teprve začínám). Do manuálu jsem samosebou koukal.
Já ale u role nepotřebuji zjištovat její děti, ale jejího rodiče pro dynamickou správu ACL v nette (role dědí od role)
Editoval Joacim (3. 2. 2016 9:04)
- Joacim
- Člen | 229
akadlec napsal(a):
Tak si to nechej jako v tom první příspěvku a musí ti to stačit. Budeš v entitě vidět jak její potomky tak i z potomku uvidíš na rodiče.
Otázkou je, zda to nemám vyřešit ve fasádě a bylo by to pro mě jednodušší, v entitě bych měl pouze potomky daného rodiče v kolekci a ID rodiče, daného rodiče(pokud má rodiče)
Array Collection mužu mít jen na potomky
Na rodiče se budu ptát ve fasádě entity
Jen u OneToMany se vytváří ArrayCollection → což jsou děti
Takže budu mít ve fasádě getRole ($id) a getParent($id)
Editoval Joacim (3. 2. 2016 9:17)
- Joacim
- Člen | 229
F.Vesely napsal(a):
Staci zmenit parent na:
/** * Sloupec pro ID rodiče (PARENT) * @ORM\ManyToOne(targetEntity="AclRole") */ protected $parent;
To bych měl ale pak OneToMany na ID, mohu mít v doctrine entitě definován ID a pak AC_children kde bych se odkazoval na ID
/**
* Sloupec pro ID role.
* @ORM\Id
* @ORM\Column(type="integer")
* @ORM\GeneratedValue
*
*/
protected $id;
/**
* Sloupec pro jméno role.
* @ORM\Column(type="string")
*/
protected $name;
/**
* Sloupec pro ID rodiče (PARENT)
* @ORM\Column(type="integer")
* @ORM\ManyToOne(targetEntity="AclRole")
* @JoinColumn(name="parent_id", referencedColumnName="id")
*/
protected $parent;
/*@ORM\OneToMany(targetEntity="AclRole", mappedBy="parent_id")*/
protected $ac_children
Pro danou entitu bych měl její ID, Název, rodiče(ID rodiče) a pak v ArrayCollection Všechny její potomky a UserFasade bych měl jen metodu kde bych se ptal na rodiče dané role pro potřeby Dynamického ACL
Editoval Joacim (3. 2. 2016 10:58)
- F.Vesely
- Člen | 369
Nechapu, proc tam mas ty potomky a jeste k tomu na entitu User? To, ze je to uvedene v prikladu, jeste neznamena, ze to potrebujes. Ty potrebujes znat parent, protoze od nej dedis prava, children jsou ti k nicemu.
Overeni, jestli ma dana role na neco prava, si dej primo do te entity.
- Joacim
- Člen | 229
F.Vesely napsal(a):
Nechapu, proc tam mas ty potomky a jeste k tomu na entitu User? To, ze je to uvedene v prikladu, jeste neznamena, ze to potrebujes. Ty potrebujes znat parent, protoze od nej dedis prava, children jsou ti k nicemu.
Ok, zatím mám v entitě AclRole pouze napojeni na user. Potomci měli být na AclRole (byla to chyba které jsem si nevšimnul a již ji opravil) – potomci u každé role jsou mi zatím k ničemu to vím, že potřebuji rodiče daného potomka
Dynamická správa rolí a zdrojů
Overeni, jestli ma dana role na neco prava, si dej primo do te entity.
To jsem si myslel, že má být ve fasádě, v entitě dle dokumentace by měli být jen setry getry a validace ? Jen se ptám
navíc mi není jasné jak pořešit napojení doctrine a nette
Co budou Service, co Fasády a co Repositáře ? Nebo používáte jen Entity
a fasády ?
Např: Třída BrowserInfo je service ? Vrací pouze informace
o prohlížeči
Editoval Joacim (3. 2. 2016 13:00)
- F.Vesely
- Člen | 369
Joacim napsal(a):
To jsem si myslel, že má být ve fasádě, v entitě dle dokumentace by měli být jen setry getry a validace ? Jen se ptám
Ne, Entita by mela mit chovani napriklad isAllowed(), authenticate(), atd. A settery moc nedoporucuji pouzivat, radsi si je pojmenuj podle toho co delaji changePassword(), rename(), move(), atd. Pro povinne promenne pouzivej konstruktor.
- Joacim
- Člen | 229
akadlec napsal(a):
Řek bych že si to zbytečně komplikuješ a hledáš problémy tam kde nejsou. Normálně si vytáhneš entity a přes getParent si pak přiřadíš těm rolím parenty v registraci rolí.
Jo asi máš pravdu, třeba u entit dědím z BaseEntity je to kvůli tomu abych nemusel mít v entitě getry a setry (Kdyby se o to postará, bohužel netbeans to neumějí), tedy mohu mít
// Entita
protected $name;
//Fasáda
$en = new Entita();
$en->name = "Test"; // i kdyz je protected
Chtěl jsem použít 5 vrstvý model "VIZ ":https://www.zdrojak.cz/…ine-2/článek
App
--Model
-------Entities (entita = tabulka nejčastěji)
-------Facades (logika postavená nad repositories a services)
-------Repositories (orm dotazy)
-------Services (logika jako je třída Email(pro odeslání emailu), BrowserInfo (vrací informace o prohlížeči, verzi, ip adresu))
Moje otázky
- Co je a co není servisa ? Mám třídu Utils, Mailer, BrowserInfo (u těch si myslím že stačí jedna instance)
- Pokud budu mít Fasádu pro entitu a používám složití dotazy mám tyto dotazy ukládat jinam ?
// Ani nevim jak ji pojmenovat ???
// Pokud existuje záznam mladší než 30 minut vrat záznamy jinak vrat null a vytvoř nový záznam
// metoda v RestrictionFacade
private function getLoginRestricterByIp($ip) {
$params = array("ip" => $ip, "datetime" => date('Y-m-d H:i:s', strtotime("-30 minutes"))); //(NOW() - INTERVAL 30 MINUTE)
return $this->em->createQueryBuilder()->from(LoginRestricted::class, 'l')->where('ip = :ip AND attempt < 3 AND created >= :datetime')->setParameters($params)->orderBy('id', 'DESC');
}
// Nette database
$record = $this->database->table('login_restricted')->where('ip_address = ? AND numberOfRetries < 3 AND created >= (NOW() - INTERVAL 30 MINUTE)', $ip_address)->order('id DESC');
- Lze si v nette vytvořit anonymní objekt, ke kterému bych mohl libovolně
vytvářet proměnné objektu, abych mohl vytvořit objekt(který nemá třídu)
toho co chci v entitě zeditovat a pak ji to jen předal(abych nemusel
používat array, tedy
$data["id"]
)
$data = new stdClass();
$data->id = 1;
$data->test = "test";
$this->editArticle($object, $data);
public function editArticle(Article $article, $data)
{
$article->title = $data->title;
$article->category = $data->category;
$article->content = $data->content;
$this->em->flush();
}
- Jak v nette přepsat SQL výraz (NOW() – INTERVAL 30 MINUTE) přes Nette|Util|DateTime?
date('Y-m-d H:i:s', strtotime("-30 minutes")) // zatim přepsano takto
Editoval Joacim (3. 2. 2016 18:58)
- Joacim
- Člen | 229
F.Vesely napsal(a):
- Neni presne dane, co musis pouzivat. Ja treba moc Facade nepouzivam.
- Ja jsem si dost oblibil Query Objecty z Kdyby
- Muzes pouzivat stdClass nebo Nette/Utils/ArrayHash.
(new DateTime)->modify("-30 minutes")
Díky moc za odpovědi na všechny dotazy, nesmírně mi to pomohlo.
Query Object je vlastně Doctrine QueryBuilder(ten už používám na
jednoučelové dotazy a pod).
Snad už poslední dotaz: když mám fci
public function editArticle(Article $article, $data)
{
$article->title = $data->title;
$article->category = $data->category;
$article->content = $data->content;
$this->em->flush();
}
a neměl bych např definovaný atribut $data->category, nastaví se mi entyta a uloží do db sloupec $article->category jako hodnota null nebo u tohoto sloupce zůstanou hodnoty nezměněny.
Jde mi o to zda je fce edit použít univerzálně pro editacijak všech sloupců tak třeba jen dvou aniž bych musel volat editSloupec1 a pak editSloupec2
Editoval Joacim (3. 2. 2016 19:41)
- Joacim
- Člen | 229
F.Vesely napsal(a):
Nastavis hodnotu na null, tak se ti taky pri flush() ulozi do db jako null.
To jsem si myslel, díky moc.
Takže pokud chci editovat pro danou entitu jen 3 položky z 10 musím si
napsat novou metodu a nebo ošetřit každou proměnou třídy entity isset
($data->promena) což není pekny