Nextras\ORM – ORM nad Nextras\Dbal

Hanz25
Člen | 38
+
0
-

Jde nějak určit jméno spojovací tabulky a ne jenom patternu? Protože když třeba udělám vztah m:n pro přátelství, vznikne tabulka s neintuitivním jménem users_x_users a navíc už vlastně nemohu mít druhý takový vztah.

Díky

hrach
Člen | 1834
+
+1
-

@Hanz25 ano, v mapperu pretizis metodu, viz. doc https://nextras.org/…/conventions#…

meridius
Člen | 34
+
0
-

hrach napsal(a):

@meridius jedna se jen o like, nebo i o nejake jine custom operace?

@hrach zatím mě trápí jen LIKE, ale obecně by asi nebylo špatné, kdyby se dalo kolekci filtrovat jakýmikoliv uživatelskými podmínkami.

hrach
Člen | 1834
+
0
-

@meridius problem je, ze pak by ti takova metoda nemusela fungovat v testech, respektive bys ji musel implementovat i pro array mapper :)

Hanz25
Člen | 38
+
0
-

@hrach paráda, to je super

Hanz25
Člen | 38
+
0
-

Ahoj,

plánujete i do trojky zařadit podporu víceslovných camel case názvů entit/repo/mapperů ?

Několikrát jsem to zkoušel a myslím, že to failuje někde v mapperu, kdy to ten název vždy přepisuje na ucfirst název.

Díky :)

Editoval Hanz25 (15. 5. 2017 13:55)

hrach
Člen | 1834
+
0
-

@Hanz25 melo by to fungovat, respektive si nejsem vedom nejakeho bugu nebo omezeni. Idelani by bylo, kdybys otevrel issue s kokretnim usecasem,jak se to chova a jak si predstavujes, ze se to chovat ma.

Hanz25
Člen | 38
+
0
-

@hrach Aha, já myslel, že pro to prostě není podpora. Ok, zkusím to jestli to nebude fungovat, hodím to do issue

Hanz25
Člen | 38
+
0
-

Aha tak to se omlouvám za mystifikaci, možná to byl bug z doby inflectoru :)

Jinak sleduji práce na 3.0 a vypadá to moc dobře, fandím vám.

Lizardor
Člen | 35
+
0
-

Zdravím, mám jenom dotaz ohledně removeAndFlush() pokud to zavolám nad Entitou která má cizí klíče, tak pro mě tam jsou zbytečně selecty na tabulkami právě těch cizích klíčů (další dotazy navíc), které se vygenerují před transakcí, dá se to případně nějak zakázat?

andros
Člen | 145
+
0
-

Ahoj, snažím se naučit a používat Nextras ORM a už druhý den se trápím s tímto:

Mám entitu Categorie:

/**
 * Category
 * @property int $id {primary}
 * @property string $name
 * @property OneHasMany|Movie[] $movies {1:m Movie::$cat}
 */
class Category extends Entity {

}

Pak mám entitu Filmy:

/**
 * Class Movie
 * @property int $id {primary}
 * @property string $title
 * @property int|NULL $category
 * @property string|NULL $content
 * @property int|NULL $csfdId
 * @property string|NULL $year
 * @property DateTimeImmutable $createdAt
 * @property OneHasMany|Comment[] $comments {1:m Comment::$movies }
 * @property ManyHasMany|Tag[] $tags {m:m Tag::$movies, isMain=true}
 * @property ManyHasOne|Category[] $cat {m:1 Category::$movies}
 */
class Movie extends Entity {
}

Jeden film může mít přiřazenou jednu kategorie, jedna kategorie, může mít x filmů
V šabloně mám zhruba toto:

{foreach $movies as $movie}
$movie->title
$movie->cat->name (tady chci zobrazit název kategorie)
{/foreach}

Tabulky v DB mám takto:

CREATE TABLE `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


CREATE TABLE `movies` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category` int(11) NOT NULL,
  `title` varchar(255) DEFAULT NULL,
  `content` text,
  `csfd_id` int(11) DEFAULT NULL,
  `year` varchar(4) DEFAULT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `year` (`year`),
  KEY `category` (`category`),
  KEY `csfd_id` (`csfd_id`),
  CONSTRAINT `movies_ibfk_1` FOREIGN KEY (`category`) REFERENCES `categories` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Bohužel pořád dostávám chybu:
Property FrontModule\Movie::$cat is not nullable.

Prosím, napadá někoho, kde dělám chybu ?

Editoval andros (16. 5. 2017 20:35)

Hanz25
Člen | 38
+
+1
-

Ahoj,

tabulky vypadají oka, ale ta definice entity je taková mírně zmatečná. Definuješ zvlášť ten klíč jako obyčejný int sloupec a zároveň jako další property, která je ovšem vztahem.

Já bych definici té entity upravil takto

/**
 * Category
 * @property int $id {primary}
 * @property string $name
 * @property Movie[]|NULL $movies {1:m Movie::$category}
 */

/**
 * Class Movie
 * @property int $id {primary}
 * @property string $title
 * @property Category $category {m:1 Category::$movies}
 * @property string|NULL $content
 * @property int|NULL $csfdId
 * @property string|NULL $year
 * @property DateTimeImmutable $createdAt
 * @property Comment[] $comments {1:m Comment::$movies }
 * @property Tag[] $tags {m:m Tag::$movies, isMain=true}
 */

– odstranil jsem statementy OneHasMany a ManyHasOne – jsou nepovinné a zhoršují čitelnost (úplně nevím proč tam jsou)
 – sjednotil jsem property $cat a $category do jedné. Nemá cenu (a ani to snad nejde) to definovat ve dvou propertách, protože pokud chceš jen ten int, tak se dá nad property zavolat getRawValue().
 – odstranil jsem závorky [] u property category (původně cat), protože odkazuješ na jednu entitu a ty závorky značí že jich bude víc, tedy odkazují na kolekci

revager
Člen | 3
+
0
-

Ahoj,

narazil jsem na problém a vůbec nechápu, proč se to nechová tak, jak by mělo.

Mám entity User, Reservation a UserReservation

<?php
/**
 * @property int $id {primary}
 * @property Block $start    {m:1 Block, oneSided=true}
 * @property Block $end    {m:1 Block, oneSided=true}
 * @property User $user    {m:1 User, oneSided=true}
 * @property int $numberOfReservations
 * @property int $maxNumberOfReservations
 * @property DateTime $date
 * @property string $note
 */
class Reservation extends Entity
{

}
?>
<?php
/**
 * @property int $id    {primary}
 * @property string|null $uid
 * @property int $cuniPersonalId
 * @property string $email
 * @property string|null $name
 * @property string $role
 * @property string|null $data
 */
class User extends Entity
{

}
?>
<?php
/**
 * @property int $id {primary}
 * @property User $user_id {m:1 User, oneSided=true}
 * @property Reservation $reservation_id {m:1 Reservation, oneSided=true}
 * @property DateTime $created_at
 */
class UserReservation extends Entity
{

}
?>

UserReservation je spojovací tabulka

CREATE TABLE `user_reservation` (
  `id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
  `user_id` int(11) NOT NULL,
  `reservation_id` int(11) NOT NULL,
  `created_at` date NOT NULL,
  FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE CASCADE,
  FOREIGN KEY (`reservation_id`) REFERENCES `reservation` (`id`) ON DELETE CASCADE
);

Když chci získat data například tímto způsobem:

<?php
$userReservations = $this->orm->UserReservations->findBy(['reservation_id' => $reservationId]);

foreach ($userReservations as $userReservation) {
	dump($userReservation->user_id);
}
?>

tak mi to padne na chybě:

Property App\Orm\UserReservation::$user_id is not nullable.

Jediné, co mi to vrátí, je id záznamu. Zbytek je null.

Dotaz, který ORM generuje, je v pořádku a vrací přesně to, co lze očekávat. Bohužel si to nějak nerozumí s entitou.

SELECT `user_reservation`.* FROM `user_reservation` AS `user_reservation` WHERE `user_reservation`.`reservation_id` = '1'

Nevěděl by někdo, kde dělám chybu? Děkuji za jakoukoliv radu či tip :).

hrach
Člen | 1834
+
+1
-

@revager v UserReservation by se property mely jmenovat bez suffixu _id (pokud je nastaven v db spravne cizi klic). Viz dokumentace https://nextras.org/…/conventions#…

revager
Člen | 3
+
0
-

hrach napsal(a):

@revager v UserReservation by se property mely jmenovat bez suffixu _id (pokud je nastaven v db spravne cizi klic). Viz dokumentace https://nextras.org/…/conventions#…

@hrach Mockrát děkuju :)

Editoval revager (20. 6. 2017 20:48)

Hanz25
Člen | 38
+
0
-

Ahoj,

existuje někde ukázka implementace property container? Pořád kolem téhle featurky chodím, ale nevím úplně přesně jak funguje a jak by se dala implementovat.

Lizardor
Člen | 35
+
0
-

Zdravím setkal se někdo s problémem že když nastavím
dbal:
simpleStorageTz: ‚Europe/Prague‘

tak DateTime který vytvořím a uložím do DB je v pořádku, ale když ho načtu tak mám –2h.

Když ten simpleStorage nepoužiji, tak se mi od DB DateTime uloží o –2h. ale zas načte (v PHP) v klasickém času, nějáké nápady jak tohle vyřešit?

Felix
Nette Core | 1183
+
0
-

Lizardor napsal(a):

Zdravím setkal se někdo s problémem že když nastavím
dbal:
simpleStorageTz: ‚Europe/Prague‘

tak DateTime který vytvořím a uložím do DB je v pořádku, ale když ho načtu tak mám –2h.

Když ten simpleStorage nepoužiji, tak se mi od DB DateTime uloží o –2h. ale zas načte (v PHP) v klasickém času, nějáké nápady jak tohle vyřešit?

Jojo stalo, zvyknul jsem si, ze v DB to mam o trochu mene, ale verim, ze je na to lepsi reseni.

Hanz25
Člen | 38
+
0
-

Jo, to mám také.

Sem tam se mi to podaří někde vychytat když zkouším různé kombinace, ale ne všude mám povoleno instalovat mysql timezony. A když se mi to podaří vychytat tak to samé nastavení nefunguje i jinde.

Editoval Hanz25 (24. 7. 2017 11:58)

Lizardor
Člen | 35
+
0
-

Nedalo mi to a nakonec sem to vyřešil takhle

dbal:
	simpleStorageTz: 'Europe/Prague'
	connectionTz: 'Europe/Prague'

php:
    date.timezone: Europe/Prague

tohle jediný mi funguje. Jinak kde nelze nainstalovat timezony tak to asi není řešení no.

Editoval Lizardor (24. 7. 2017 13:12)

hrach
Člen | 1834
+
0
-

Podstatný je, jaký typ sloupce používáš. MySQL nebo PostgreSQL se pak chovají jinak a orm se tomu prizpusobi. Nicméně od verze 3.0 by se to mělo chovat inteligentneji.

Pokud byste chtěli poradit, hoďte sem svojí konfiguraci orm, timezonu ve které běží MySQL, ve které běží php, typ sloupců.

David Klouček
Člen | 57
+
0
-

Jakej je vlastně cíl Nextras\ORM? Když má identity mapu a je to teda plnohodnotný ORM (narozdíl od třeba LeanMapperu), je nějakej důvod ho použít místo nejrozšířenější Doctrine 2?

A ještě jedna věc, proč snad u všechny ORM autorů z Nette komunity používaj poddotazy místo joinů?

hrach
Člen | 1834
+
+3
-

@DavidKlouček

Jakej je vlastně cíl Nextras\ORM?

Dodat kvalitni ORM pro PHP.

Když má identity mapu a je to teda plnohodnotný ORM, je nějakej důvod ho použít místo nejrozšířenější Doctrine 2?

Urcite. Doctrine2 ma plno vlastnosti, ktere nemuseji vyhovovat. Uz jsem se o tom nekde rozepisoval, ale nemuzu to ted najit. Osobne mi na Doctrine nevyhovuji tyto vlastnosti, ktery ma nextras poreseny:

  • lazy loading relationships: v Doctine to napriklad nefunguje u ManyHasOne.
  • clever loading of relationship: v Doctrine na to musis myslet dopredu, v Nextras mas na repository definovany obecny gettery, vede to k trochu vic reusable architekture.
  • definice entity: je to dost vec vkusu, ale v Nextras se vleze na screen, takze se lepe ziska prehled
  • edge case vlastnosti: napr. v Doctrine nelze u MySQL ziskat id, nez udelam flush, protoze persist realne nevola jeste dotaz (insert) do db. Takova blbost, ale dost dulezita. U Postgre se chova Doctrine lepe diky sekvencim.

A ještě jedna věc, proč snad u všechny ORM autorů z Nette komunity používaj poddotazy místo joinů?

U Nextras se poddotazy naopak nepouzivaji. (Temer, jediny pouziti je pro nektere count dotazy, ale tam by join jaksi moc nepomohlo.) Pro filtering se pouziva join, pro vybrani dat se pouzivaji nove dotazy. Ale to nejsou poddotazy. :)

Editoval hrach (24. 7. 2017 20:23)

Čamo
Člen | 786
+
0
-

Zdravím,
u Nette Database je možné pridať do konfigurácie options. Napr:

	username: root
	password: root
	options:
		PDO::MYSQL_ATTR_INIT_COMMAND: 'SET sql_mode="NO_ENGINE_SUBSTITUTION"'

Je možné to pridať aj do dbal sekcie?

dbal
	...
	password:
		username: root
		options:
			PDO::MYSQL_ATTR_INIT_COMMAND: 'SET sql_mode="NO_ENGINE_SUBSTITUTION"'

Ďakujem.

stajo
Člen | 8
+
0
-

zdravim,

ako v teste vytvorim entitu bez hlasky:
Nextras\Orm\InvalidStateException: MetadataStorage::get() called too early.

Felix
Nette Core | 1183
+
0
-

stajo napsal(a):

zdravim,

ako v teste vytvorim entitu bez hlasky:
Nextras\Orm\InvalidStateException: MetadataStorage::get() called too early.

Been there: https://github.com/…m/issues/181

Aktualne to neni mozne, ale premyslim o tom, ze bych neco maleho sesmolil, aby to slo. :-)

stajo
Člen | 8
+
0
-

Been there: https://github.com/…m/issues/181

Aktualne to neni mozne, ale premyslim o tom, ze bych neco maleho sesmolil, aby to slo. :-)

nj, to som nasiel, kym som sem napisal. akurat mi neslo do hlavy, ze to je rok stare.

to sa fakt neda nejako normalne napisat unit test s entitou?

hrach
Člen | 1834
+
0
-

@Čamo Muzes sql mode nastavit primo z konfiguracniho pole, viz.: https://nextras.org/dbal/docs/3.0/. Pripadne napis, jake jine konfigurace te zajimaji.

hrach
Člen | 1834
+
0
-

to sa fakt neda nejako normalne napisat unit test s entitou?

@stajo on to pak neni moc unit test, kdyz tam passujes celou Entitu. Tak tam posilej IEntity, respektive mock tve entity.

Proste aby ORM dobre fungovalo a bylo propojene s ostatnimi entitami, potrebuje mit dobra metadata, a ty neni jednoduche vytvorit. Obecne by me zajimalo, jak ten test presne pisete? @Felix

Editoval hrach (13. 8. 2017 14:49)

Felix
Nette Core | 1183
+
0
-

hrach napsal(a):

to sa fakt neda nejako normalne napisat unit test s entitou?

@stajo on to pak neni moc unit test, kdyz tam passujes celou Entitu. Tak tam posilej IEntity, respektive mock tve entity.

Proste aby ORM dobre fungovalo a bylo propojene s ostatnimi entitami, potrebuje mit dobra metadata, a ty neni jednoduche vytvorit. Obecne by me zajimalo, jak ten test presne pisete? @Felix

Napriklad urcite workflow ve fasadach, kdy se meni stavy entity a dalsi veci. Mocky tedka vyuzivam, ale radeji bych realnou entitu.

Vyki
Člen | 388
+
0
-

Ahoj, chtěl jsem se zeptat, zda lze pomocí Nextras\Orm jednoduše získat i výsledek dotazu s agregační funkcí, nebo musím položit dotaz přes Nextras\Dbal\Connection. Jde mi o to, že chci zjistit MAX(hodnotu) a MIN(hodnotu) sloupce v tabulce a nejraději bych to měl v repository jako metodu, ale patrně by to pak popíralo smysl ORM.

Díky

David Matějka
Moderator | 6445
+
0
-

@Vyki ahoj, tohle patri do mapperu, kde napises SQL a pak se na to budes dotazovat skrz repozitar pres proxy

Vyki
Člen | 388
+
0
-

@DavidMatějka Díky za nasměrování. Nakonec jsem našel example zde: https://nextras.org/…2/repository#…

Hologos
Člen | 19
+
0
-

Ahoj,

existuje možnost si vytáhnout z ORM connection a zavolat SQL napřímo (myslím mimo mapper)?
Mám pár commandů a potřeboval bych provádět truncate tabulek a nechci to dávat do mapperu, aby to nebylo přístupné ze standardní aplikace.

Díky

hrach
Člen | 1834
+
0
-

@Hologos DbalMapper vyuzival Nextras\Dbal\Connection, takze si normalne nech nawirovat pres DI tuto tridu.

Hologos
Člen | 19
+
0
-

@hrach Napsal jsem to blbě – chtěl bych si vytvořit db tabulky pomocí Symfony Console a chtěl jsem si nějak z ORM vytáhnout Nextras\Dbal\Connection. Nenašel jsem na to žádnou metodu, jedině vytvořit Mapperu metodu, která to vrátí, ale to se mi moc nelíbilo, protože to je na testy a nechci, aby to bylo přístupné z aplikace. Nakonec jsem to udělal přes inject property. I tak mě zajímá, jestli je nějak možné se dostat k Nextras\Dbal\Connection z Modelu (Nextras\Orm\Model\Model).

Hologos
Člen | 19
+
0
-

Ahoj,

mám obrovský problém s výkonností ORM. Udělal jsem sample aplikaci, kde trasuju časy. Na lokálu je PHP 5.6.31, na hostingu 5.6.30. Používám Nextras/ORM 2.2 a Nextras\Dbal 3.0.

V DB jsou 2 tabulky Set a Cards. Tabulka sets má 1 záznam, tabulka cards má asi 300 záznamů.

Entity

<?php

namespace App\Model\Orm;

use Nextras\Orm\Entity\Entity;
use Nextras\Orm\Relationships\OneHasMany;

/**
 * @property int                $id {primary}
 * @property string             $code
 * @property string             $name
 * @property string             $type
 * @property DateTime           $releaseDate
 * @property int                $active {default self::NOT_ACTIVE}
 * @property OneHasMany|Card[]  $cards {1:m Card::$set, orderBy=name}
 */
class Set extends Entity
{
    const NOT_ACTIVE = 0;
    const ACTIVE = 1;
}

/**
 * @property string         $id {primary}
 * @property string|NULL    $multiverseId
 * @property string         $name
 * @property string|NULL    $number
 * @property string|NULL    $imageName
 * @property string         $rarity {enum self::RARITY_*}
 * @property int            $active {default self::NOT_ACTIVE}
 * @property Set            $set {m:1 Set::$cards}
 */
class Card extends Entity
{
    const NOT_ACTIVE = 0;
    const ACTIVE = 1;

    const RARITY_COMMON = 'c';
    const RARITY_UNCOMMON = 'u';
    const RARITY_RARE = 'r';
    const RARITY_MYTHIC = 'm';
    const RARITY_SPECIAL = 's';
    const RARITY_BASIC_LAND = 'b';
}
?>

Tabulky mají v databázi indexy:

sets
PRIMARY id
UNIQUE code
INDEX type
INDEX release_date

cards
PRIMARY id
INDEX set, name
INDEX name

<?php
$sets = $this->orm->sets->findBy(['type' => 'expansion'])->orderBy('releaseDate', ICollection::DESC);

Debugger::timer('overall');

foreach($sets as $set)
{

    Debugger::timer('cards');
    $cards = $this->sets->cards;
    foreach($cards as $card) {} // aby se provedlo načtení
    Debugger::log('Retrieving cards took '. number_format(Debugger::timer('cards'), 2, ',', ' ') .'s.');

    foreach($cards as $card)
    {
        // zatim neni potreba resit
    }
}

Debugger::log('Overall time '. number_format(Debugger::timer('overall'), 2, ',', ' ') .'s.');
?>

A výstup je

<?php
Retrieving cards took 6,53s.
Overall time 6,68s.
?>

Zkoušel jsem to nahrát i na hosting, a tam jsou časy ± totožné. Zajímavé je, že když se podívám to Tracy DebugBaru, tak tam ty SQL dotazy se vykonaj za pár milisekund. Jestli to chápu správně, tak má takovou režii samotný ORM framework? Cílový stav aplikace, že tabulka sets bude mít cca 400 záznamů a tabulka cards bude mít cca 35 000 záznamů. Pokud by časy ještě více stoupaly, tak to je skoro neúnosné.

<?php
SQL query	Time (ms)
SELECT `sets`.* FROM `sets` AS `sets` WHERE `sets`.`type` = 'expansion' ORDER BY `sets`.`release_date` DESC	0.423
SELECT `cards`.* FROM `cards` AS `cards` WHERE `cards`.`set` IN (1) ORDER BY `cards`.`name`	0.730
?>

Editoval Hologos (18. 9. 2017 14:35)

hrach
Člen | 1834
+
0
-
  • uplne si nejsem jist, ze je nextras orm 2.2 kompatibilni s dbalem 3.0
  • v php 7.0 je vse dvojnasobne rychlejsi, to jen sidenote
  • vzhledem k tomu, ze dbal 3.0 vyzaduje php 7.0, nevim, jak ti to muze fungovat
  • nedava smysl, aby neco trvalo takto dlouho, muze to byt bug, muze to byt neco spatne, byt nevim moc co
  • mas vypnuty xdebug?
  • totozne casy na hostingu – to jen spis dava tusit, ze je neco spatne, pokud teda nemas spatny hosting a vyborne pc
  • pripadne muzes zkusit nextras orm demo, vytvorit 300 komentaru k clanku a mrknout cas nacitani dany stranky, jestli je taky tak spatny.
Lizardor
Člen | 35
+
0
-

Zdravím chci se zeptat zda-li se někdo nesetkal s tímto problém. Jde o to že vybírám velké množství řádku (cca 25k) a limituju to třeba na 1000, dotaz je rychlý v clientu a v tracy taky, cca 16–20ms (je tam hodně joinu) ale pokud si to profiluju (i na produkci), tak se dostanu na 200–300ms konkrétně tahle část

$result = $this->connection->queryArgs($builder->getQuerySql(), $builder->getQueryParameters());

Pokud dotaz napíšu klasickým query je výsledek totožný. Není tam nějáký problém s „LIMIT“ ?

Editoval Lizardor (21. 9. 2017 16:57)

hrach
Člen | 1834
+
0
-

@Lizardor hm, z tveho popisu moc netusim. Obecne je velmi pomala hydratace, ale samotny select pred nextras dbal by mel byt rychly. chtelo byt o nejak jeste prodebugovat, co je teda presne problem.

majky358
Člen | 37
+
0
-

Mám vzťah M:1, názov stĺpcu v zdrojovej tabuľke je user_account_id, názov property musím mať $userAccount, používam prefixy aj v iných tabuľkách.

* Class UserMessage
* @property User $user {m:1 User::$messages}

* Class User
* @property UserMessage[]|NULL $messages {1:m UserMessage::$user}

//Result: UserMessage::$user is not nullable

//funguje OK
* Class UserMessage
* @property User $userAccount {m:1 User, oneSided=true}

V createStorageReflection volám setMapping a zmením názov, nejaký iný elegantnejší spôsob ? :)

Editoval majky358 (25. 9. 2017 16:26)

hrach
Člen | 1834
+
0
-

@majky358 nechapu to moc; tak ci onak: nazvy sloupcu musi sedet s db, jedinou vyjimkou jsou cizi klice, kdy se automaticky stripuje suffix _id (nebo Id pri camel case). Zbytek je treba nastavit v mappingu. UserMessage[]|null je spatne, to null tam nikdy nemuze byt.

Zax
Člen | 370
+
0
-

Ahoj, narazil jsem na jednu takovou WTF vlastnost, která snad ani není bug, jen prostě… WTF.

Jde o to, že v některých situacích čtení z property zavolá její setter.

__get()getValue()internalGetValue()initProperty()internalSetValue() → setter*()

Což může vést k zajímavým „efektům“, např.:

<?php
/**
 * @property int $id {primary}
 * @property OneHasMany|Child[] $children {1:m Child::$user}
 */
class User extends Entity {}

/**
 * @property int $id {primary}
 * @property User $user {m:1 User::$children}
 * @property string $personalId
 */
class Child extends Entity {

	public function __construct(User $user, $personalId) {
		$this->user = $user;
		$this->personalId = $personalId;
	}

	protected function setterPersonalId($value) {
		$this->throwIfDuplicate($value);
		return $value;
	}

	private function throwIfDuplicate($personalId) {
		foreach($this->user->children as $child) {
			if($child === $this) {
				continue;
			}
			if($child->personalId === $personalId) { // problém - zavolá setterPersonalId nad $child a spustí celý kolotoč znovu
				throw new DuplicateChildException;
			}
		}
	}

}
?>

Nejspíš to jen blbě používám, ale každopádně mi to přijde, že se taková chyba může stát každému celkem snadno. Mít validaci v setteru mi přijde logické (resp. jde o pochopitelný zvyk z klasických public setterů), naopak bych nečekal, že se bude volat setter když čtu – nicméně ono to zjevně svůj důvod má, proto mi to vlastně nepřijde jako bug a hádám, že to není rozumně řešitelný.

Jinak musím ORMko pochválit, jedním očkem sleduju vývoj od začátku (fíha, to už uběhly 3 roky?) a vypadá super! Na menší projekty kde člověk nechce vytahovat Doctrinu vypadá jako velmi dobrá volba. Díky :-)

Editoval Zax (28. 9. 2017 0:40)

Milan Obrtlík
Člen | 50
+
0
-

Mějme SQL dotaz

select * from book
join book_x_tag as bt on book.id = bt.book
join tag on tag.id = bt.tag
join book_x_author as ba on book.id = ba.book
join author author.id = ba.author
where author.id = 1 and tag.id IN (1,2,3,4,5,7)

Pomocí tohoto dotazu získám knihy, které napsal autor 1 a zároveň mají tagy 1 až 7.
Poradíte mi prosím jak tento dotaz přepsat do Nextras/ORM?

Čamo
Člen | 786
+
0
-

Zdravím,
prosím vás čo robím špatne na používaní builderu v maperi?

public function findMedicalPlansToProgressComponent(  )
{
	$this->builder()
		->andWhere('start < %dt', DateTime::from('now'))
		->andWhere('end > %dt', DateTime::from('now'));

	return $this->builder();
}

Ono to vracia kolekciu ale vôbec to neberie do úvahy tie podmienky.
Vypadne z toho sql

SELECT `medical_plans`.* FROM `medical_plans` AS `medical_plans`

A to ešte netuším ako do toho dostanem join …

Editoval Čamo (3. 10. 2017 16:39)

Čamo
Člen | 786
+
0
-

Aha tak kolega mi poradil, že treba proste priradiť $builder = this->builder()...

hrach
Člen | 1834
+
0
-

@Zax yes, to je aktualni chovani a je o tom, ze proste nacitani dat z db musi projit setterem (aby se treba nejaka data znormalizovala). Mel jsem pocit, ze uz jsem kolem toho neco resil do 3.0, ale nemuzu ted najit zadny konkretni commmit. Mozna by se to mohlo nejak zmenit. Muzes zkusit pouzit metodu getRawValue(), ktera nespusti ten setter (ale muzes dostat nenormalizovanou hodnotu, coz by u stringu ale nemel byt moc problem).

@MilanObrtlík no, dle tveho popisu to podle me je o tom, ze nema mit tagy 1–7, ale jeden tak v 1–7. Jinak nevim, na jake vrstve te to zajima, ale tento konkretni dotaz by mohl byt nasledovne:

$booksRespository->findBy(['this->authors->id' => 1, 'this->tags->id' => [1, 2, 3, 4, 5, 6, 7]]);

@Čamo je to tak :-) Metoda builder() vzdy vraci novou instanci builder.

majky358
Člen | 37
+
0
-

Mám dve entity, vzťah majú 1:1

//Address => @property User $user {1:1 Address::$address, isMain=true}
//User => @property Address $address {1:1 User::$user}

$user = new User();
//...
$userRepository->persist($user);

$address = new Address();
$address->user = $user;
$addressRepository->persist($address);

$user->address = $address;
//$user->setAsModified() | $userRepository->doPersist($user) | $userRepository->persist($user) => neprejde UPDATE

$model->flush();

Aký je správny postup ak chcem spraviť update user entity ? Došiel som len na riešenie volať $addressRepository->persist($address, $withCascade = false); ale aj tam som mal narazil na problém, že sa mi spraví udpate entity ale ref entity address nieje obsiahnutá v update dotaze. Ďakujem

Editoval majky358 (5. 10. 2017 1:27)

Hologos
Člen | 19
+
0
-

Ahoj,

narazil jsem na zajímavý problém a nevím, jak je vyřešit. Mám 2 entity, které jsou mezi s sebou 2× provázané vazbou ManyHasMany a generuje mi to pro oba případy shodné názvy vazebních tabulek. Jak z toho ven?

Skupina (Group) může mít 1 až N vlastníků (User) a vlastník (User) může vlastnit 0 až N skupin.
Skupina (Group) se skládá z 0 až N hráčů (User) a hráč může být v 0 až N skupinách (Group).

<?php

namespace App\Model\Orm;

use Nextras\Orm\Entity\Entity;
use Nextras\Orm\Relationships\ManyHasMany;

/**
 * @property int $id {primary}
 * @property ManyHasMany|Group[] $ownedGroups {m:m Group::$owners, isMain=true}
 * @property ManyHasMany|Group[] $groupsIamMemberOf {m:m Group::$members, isMain=true}
 */
class User extends Entity
{

}

/**
 * @property int $id {primary}
 * @property ManyHasMany|User[] $owners {m:m User::$ownedGroups}
 * @property ManyHasMany|User[] $members {m:m User::$groupsIamMemberOf}
 */
class Group extends Entity
{

}
?>

Edit: V jedné přednášce (ještě ke starému Orm) si zmínil, že bys to řešil pomocí více entit. Když nad tím přemýšlím, tak je to tak určitě lepší pro budoucí vývoj, protože se pak může změnit požadavek, že GroupMember nemusí být nutně registrovaný uživatel. I tak to tu nechám, zajímá mě aktuální názor.

Editoval Hologos (13. 10. 2017 9:03)