tiny ‘n’ smart
database layer

Odkazy: dibi | API reference

Oznámení

Omlouváme se, provoz fóra byl ukončen

Lean Mapper – tenké ORM nad dibi

před 5 lety

Tharos
Člen | 1042

@jannek19: Ahoj, je více možností, jak tohle vyřešit. V MySQL databázi bych to já řešit asi tak, že bych měl ten sloupec nadefinovaný takto:

created DATETIME DEFAULT CURRENT_TIMESTAMP

Property bych pak měl read only a vůbec ji ručně nenastavoval. Bohužel výše uvedená definice funguje asi až od MySQL 5.6.5…


Jinak bych to asi řešil v repositáři přetížením protected metody Repository::insertIntoDatabase. Na entitě bych tu položku měl opět nadefinovanou jako read only a v té přetížené metodě v repositáři bych tu hodnotu doplňoval. V podstatě bych si tu miniaturní metodu (má 6 řádků) napsal pro relevantní typ entit nanovo.

před 5 lety

janpecha
Člen | 58

Ahoj, protože už mě fakt nebaví prohrabávat toto vlákno, tak jsem si dneska začal psát takovou vlastní „dokumentaci“ k LeanMapperu. Prozatím je to stále ve stádiu tvorby, hotová je základní podoba webu, samotná dokumentace obsahuje nyní pouze základní odkazy na stažení jednotlivých verzí, API dokumentace a odkazy na některá rozšíření. Postupně chci doplnit informace o entitách (základy, možnosti a přehled anotací, …), repozitářích, rady a postupy, co tu kdo publikoval, apod.

Pokud by měl někdo zájem zapojit se, budu rád, kompletní obsah je na GitHubu. Samotný web pak zde.

před 5 lety

Tharos
Člen | 1042

@duke: Stále nosím v hlavě ten problém týkající se datumu 0000-00-00. Implicitní passThru by to řešit a vážně nad ním popřemýšlím, ale na co bys vůbec takový datum převáděl? Na 30. 11. –0001? Není to trochu „ujeté“?

V dokumentaci k MySQL píšou, mimo jiné, následující:

“Zero” date or time values used through Connector/ODBC are converted automatically to NULL because ODBC cannot handle such values.

A tak přemýšlím, jestli by nebylo nejlepší takové nesmyslné datum převádět na NULL (rovnou je vnímat jako NULL)…

před 5 lety

tomas.lang
Člen | 54

@Tharos: ja bych se za automaticke konvertovani na NULL urcite primlouval, ale jen za prepokladu ze bude v property uvedeno DateTime|null; jinak by to asi opravdu chtelo prevest na neco stylu 30. 11. –0001…

před 5 lety

majkl324
Člen | 13

Před nějakou dobou jsem začal používat LeanMapper a nemůžu si ho vynachválit. Řeším ale jeden problém, který nejsem schopný nějak doladit.

Mám entitu Product a další entitu a tabulku s obrázky, které jsou ke každému produktu přiřazeny. Pokud chci získat všechny obrázky, vše funguje jak má:

<?php
/**
 * @property-read int $id
 * @property User $author m:hasOne(user_id)
 * ...
 * @property-read ProductImage $representImage
 * ....
 * @property-read ProductImage[]|null $images m:belongsToMany
 */
class Product extends BaseEntity
{
}
?>

Problém ale mám, pokud chci získat jenom jeden „reprezentační“ obrázek, respektive obrázek jako náhled v nějakém menu. Napadlo mě připsat vnořený SELECT do repository, ale řekl bych, že jsou určitě efektivnější způsoby jak k danému SQL přidat vnořený SELECT, který by z tabulky s obrázky vybral pouze jeden a použil se jen v případě, kdy bych representImage volal, ovšem nevím si s tím moc rady, i když si říkám, že řešení musí být jednoduché.

Díky za radu :)

před 5 lety

castamir
Člen | 631

@majkl324 mohl bys prosím popsat schéma a co by měl vracet ten representImage?

před 5 lety

majkl324
Člen | 13

Omlouvám se, tabulky jsou 2

  • Product (id, author_id, title…)
  • Product_Image (id, product_id, filename, upload_date)

Jeden product má více obrázků a representImage by měl vracet právě jeden obrázek (filename), byť jich je několik (měl by sloužit jako náhled), teoreticky je i jedno který.

To SQL, co jsem zkoušel napsat v repositáři by mohlo vypadat nějak takto

<?php
        $row = $this->connection->select('*')
            ->select(
                $this->connection->select('[product_image.file_name]')->from('[product_image]')
                    ->where('[product_image.product_id] = ' . '[' . $this->getTable() . '.id]')
                    ->orderBy('[' . $this->getTable() . '.id]')
                    ->limit(1)
            )->as('representImage')
            ->from($this->getTable())
            ->where('id = %i', $id)
            ->fetch();
?>

Z toho je už snad i můj záměr patrný a takto to funguje, ale zdá se mi to hrozně špatně řešené.. Nejsem si ani jistý, jestli je návrh těch tabulek správný, vše se to snažím učit takto za chodu, díky za trpělivost.

Editoval majkl324 (9. 3. 2014 16:53)

před 5 lety

castamir
Člen | 631
<?php
/**
 * @property-read int $id
 * @property User $author m:hasOne(user_id)
 * ...
 * @property-read ProductImage $representImage
 * ....
 * @property-read ProductImage[] $images m:belongsToMany
 */
class Product extends BaseEntity
{
    public function getRepresentImage() {
        if (reset($this->images) !== FALSE) {
            $firstImage = current($this->images);
            return $firstImage;
        }
        return NULL;
    }
}
?>

pár poznámek:

  • property $images nemusí být nutně read-only
  • u vazeb m:belongsToMany nemusíš mít alternativní typ NULL – vrací to pole a to pole bude prázdné, pokud žádný vázaný záznam neexistuje… NULL je proto zbytečný
  • pokud máš getter, tak se prioritně použije pro získání hodnoty

Editoval castamir (9. 3. 2014 17:34)

před 5 lety

Tharos
Člen | 1042

@majkl324: Ještě castamira doplním. Pokud bys nechtěl načítat z úložiště při přístupu k tomu jednomu reprezentativnímu obrázku všechny ostatní, můžeš použít následující „strategii“:

/**
 * ...
 * @property ProductImage[] $images m:belongsToMany
 * @property-read ProductImage|null $representImage m:belongsToOne(#union)
 */
class Product extends BaseEntity
{

    public function getRepresentImage()
    {
        return $this->getValueByPropertyWithRelationship('representImage', new Filtering(function (Fluent $statement) {
            $statement->orderBy('rating')->desc()->limit(1);
        }));
    }

}

Takhle docílíš přesně toho, co potřebuješ, a jako bonus Lean Mapper vygeneruje hezké dotazy. V tom anonymním filtru jsem naznačil, že si můžeš za reprezentativní obrázek snadno vybrat třeba takový, který má nejlepší rating atp.

Edit: Ještě jsem kód opravil. :) Teď už by to mělo být správně…

Editoval Tharos (9. 3. 2014 20:15)

před 5 lety

Pavel Macháň
Člen | 285

@Tharos: Jak nejlip elegantne docilit tohoto?:
Máme 2 tabulky: product a product_price kde produkt ma vice zaznamu v product_price (z duvodu historie cen kvuli fakturam atd) a chci pokud vypisuju cenu produktu $product->actualPrice (@property-read) dostat poslední záznam (nejnovejsi) z té historie cen.

Editoval EIFEL (9. 3. 2014 20:45)

před 5 lety

Tharos
Člen | 1042

@EIFEL: Je to dokonalá analogie výše uvedené záležitosti. :)

Nejsnáze (na počet řádků) to lze zhruba takto:

/**
 * ...
 * @property-read ProductPrice|null $actualPrice m:belongsToOne(#union)
 */
class Product extends BaseEntity
{

    public function getActualPrice()
    {
        return $this->getValueByPropertyWithRelationship('actualPrice', new Filtering(function (Fluent $statement) {
            $statement->orderBy('date')->desc()->limit(1);
        }));
    }

}

Píšu to úplně z hlavy, ale mělo by to fungovat. Kdyžtak mi to reklamuj. :)

před 5 lety

Tharos
Člen | 1042

tomas.lang napsal(a):

@Tharos: ja bych se za automaticke konvertovani na NULL urcite primlouval, ale jen za prepokladu ze bude v property uvedeno DateTime|null; jinak by to asi opravdu chtelo prevest na neco stylu 30. 11. –0001…

Moje představa byla právě taková, že by se u nullable položky databázová hodnota se samými nulami převedla na null, zatímco u ne-nullable položka by to celé skončilo výjimkou.

Převádění nevalidního data (a i dokumentace k MySQL to otevřeně přiznává, mimo jiné tím, že takové datum pak nelze použít ve funkcích) na cokoliv jakoby validního (30. 11. –0001) se mi trochu příčí…


Rád se nechám přesvědčit výhody/nevýhody toho či onoho řešení. Výhody výše uvedeného řešení vidím v tom, že je tahle anomálie MySQL „podchycena“, ale zároveň se pak v aplikaci nepracuje s datem, který nemá moc smysl.

před 5 lety

Tharos
Člen | 1042

janpecha napsal(a):

Ahoj, protože už mě fakt nebaví prohrabávat toto vlákno, tak jsem si dneska začal psát takovou vlastní „dokumentaci“ k LeanMapperu. Prozatím je to stále ve stádiu tvorby, hotová je základní podoba webu, samotná dokumentace obsahuje nyní pouze základní odkazy na stažení jednotlivých verzí, API dokumentace a odkazy na některá rozšíření. Postupně chci doplnit informace o entitách (základy, možnosti a přehled anotací, …), repozitářích, rady a postupy, co tu kdo publikoval, apod.

Pokud by měl někdo zájem zapojit se, budu rád, kompletní obsah je na GitHubu. Samotný web pak zde.

Je skvělé, že máš vůli pomoct s dokumentací!

Popravdě jsi mě svým přispěním trochu nakopl, protože by nebylo optimální, kdyby se nám dokumentace příliš roztříštila. Chápu, že svou pomalostí tomu dost nahrávám, ale právě jsi mě trochu popohnal. :)

Přes víkend jsem „hodil na papír“ (GitHub) to, jakou by dokumentace mohla mít optimálně strukturu, a podle toho jsem předpřipravil Wiki na GitHubu. Jsem pro použít GitHubovou Wiki, minimálně pro sepsání první verze kompletní dokumentace.

Zítra budu trávit část dne ve vlaku, a tak se zavazuji, že doplním obsah této stránky a pokusím se třeba i nějak více „vykopnout míč“.

před 5 lety

Pavel Macháň
Člen | 285

Tharos napsal(a):

@EIFEL: Je to dokonalá analogie výše uvedené záležitosti. :)

Nejsnáze (na počet řádků) to lze zhruba takto:

/**
 * ...
 * @property-read ProductPrice|null $actualPrice m:belongsToOne(#union)
 */
class Product extends BaseEntity
{

    public function getActualPrice()
    {
        return $this->getValueByPropertyWithRelationship('actualPrice', new Filtering(function (Fluent $statement) {
            $statement->orderBy('date')->desc()->limit(1);
        }));
    }

}

Píšu to úplně z hlavy, ale mělo by to fungovat. Kdyžtak mi to reklamuj. :)

Otestuju a dám vědět :)

BTW nevíš kdy asi vyjde nová verze ? :) Klidně setinková ;-) Potřeboval sem zrovna hasFilter a nemůžu nějak dokopat composer aby mě loadnul dev-develop (leanmaper pouzivam v baliku s modelem a ten az includuju composerem do projektu). Povedlo se me to az pomoci „require-dev“ jak u projektu tak v modelu.

Editoval EIFEL (9. 3. 2014 21:52)

před 5 lety

Tharos
Člen | 1042

@EIFEL: Jakmile dokončím tu „infrastrukturu“ kolem Wiki, tak novou stable verzi vydám. Takže nejspíš někdy v průběhu tohoto týdne.

před 5 lety

Pavel Macháň
Člen | 285

Tharos napsal(a):

@EIFEL: Jakmile dokončím tu „infrastrukturu“ kolem Wiki, tak novou stable verzi vydám. Takže nejspíš někdy v průběhu tohoto týdne.

Super :)


Tak sem zkoušel to stím actualPrice a nechápu jednu věc. Pokud vyberu 1 produkt tak je to OK. Pokud jich vyberu víc (findAll) a dám vypsat ceny tak to chcípne z nepochopitelného důvodu.

Mám 2 produkty:

foreach($this->productFacade->getAll() as $a){
    dump(get_class($a->getActualPrice()));
}
  1. záznam: „Model\Entity\ProductPrice“
  2. záznam: „FrontModule\ProductPresenter“

Jak se tam proboha dostane presenter?

Pokud kouku na classu jen $a tak je to dobře Product

Editoval EIFEL (9. 3. 2014 23:50)

před 5 lety

Tharos
Člen | 1042

@EIFEL: Dokázal bys mi to na nějakém minimálním příkladě předvést? Zní to jako docela haluz. :) Nenapadá mě, jak by se tam nějaký presenter mohl dostat…

před 5 lety

Pavel Macháň
Člen | 285

Tharos napsal(a):

@EIFEL: Dokázal bys mi to na nějakém minimálním příkladě předvést? Zní to jako docela haluz. :) Nenapadá mě, jak by se tam nějaký presenter mohl dostat…

Edit: tak už je to jasný … OMG … tak sem jen blb …

Dobře to vrací NULL když to nenajde ten druhý záznam… Původně sem si nevšim, že tam ten záznam nebyl a zmátlo mě že to nemělo tu metodu (asi sem si nevsim, že je to null exception) tak sem se pomocí get_class kouknul co sem vlastně dostal… no a záhada je na svete

dump(get_class(null)); // = get_class() ... takže třída tam kde se to volá = presenter

Editoval EIFEL (10. 3. 2014 11:58)

před 5 lety

elden46
Člen | 37

Zdravim,

potreboval bych natuknout smer, jak resit nasledujici:
Mam 3 entity: dilo, verze, zaznam
Dilo je zakladni, z nej vazba hasmany do verze, z nej vazba hasmany do zaznam.

Priklad: Dilo je „Pan Prstenu“ (autor, datum vydani), verze je „kniha/film/komiks“ (nakladatelstvi, id. cislo), zaznam je „ABC“ (ev. cislo, fyzicke umisteni).

Vytvarim vyhledavaci formular, do ktereho si uzivatel muze zadat vyhledavaci parametry ze vsech tri entit a dostane seznam „zaznamu“ vyfiltrovanych podle zadaneho nastaveni formulare. Jak na to, abych co nejefektivneji vyuzil LM a DB? Udelat si filtrovani nad „Dilo“ a pote si pres foreach na „verzi“ a nasledne foreach na „zaznam“ upravovat vyslednou kolekci (unsetovat „dilo“) a pak vypisovat ze zbytku? Jak pak zajistit treba sortovani podle sloupcu ze „zaznam“?
Nebo si napsat dotaz na urovni dibi a prijit tak o vyhody orm?

Editoval elden46 (10. 3. 2014 13:13)

před 5 lety

duke
Člen | 637

Tharos napsal:

tomas.lang napsal:

@Tharos: ja bych se za automaticke konvertovani na NULL urcite primlouval, ale jen za prepokladu ze bude v property uvedeno DateTime|null; jinak by to asi opravdu chtelo prevest na neco stylu 30. 11. –0001…

Moje představa byla právě taková, že by se u nullable položky databázová hodnota se samými nulami převedla na null, zatímco u ne-nullable položka by to celé skončilo výjimkou.

V podstatě souhlasím, ale doporučuji na to vyčlenit metodu, kterou by šlo přetížit (něco jako handleZeroDateTime). Ve výchozí implementaci by fungovala tak, jak navrhuješ.

před 5 lety

majkl324
Člen | 13

castamir, Tharos, díky moc za radu, vše funguje :)

před 5 lety

tomas.lang
Člen | 54

@Tharos: co se týče toho zpracování „špatných“ datetime, máš asi pravdu že v případně nevalidního vstupu (bez zadaného null) by bylo lepší vyhodit nějakou jasnou vyjímku, alespoň si toho vyvojář i lépe všimne :-)

@duke: +1 (jen název bych možná volil jiný?)

Každopádně z jiného soudku – chtěl jsem se zeptat jak to máte s testováním LM entit? Totiž pro unit testy bych nerad dělal připojení k DB (kdyby nic tak z pohledu rychlosti provádění testů), ovšem díky závislosti entit na DB a nemožnosti s nimi pracovat v detached stavu (hlavně tedy s entitami s 1:N a M:N vazbami) moc jiných možností nevidím. Přitom alespoň většiná mých entit má vcelku jednoduchou doménovou logiku a rád bych je testoval, a vysouvání této logiky do externích testovatelných tříd zbytečně povede jen k „hloupým“ entitám, což je jistě škoda. Přemýšlel jsem nad možností vlastního driveru v Dibi, který by testoval správně kladené SQL dotazy a vracel odpovídající data, ale bojím se že touto cestou bych se brzy dostal do nepěkných míst… Máte tedy prosím případně někdo řešení či alespoň nápad kudy na obra? :-)

Editoval tomas.lang (10. 3. 2014 22:01)

před 5 lety

Tharos
Člen | 1042

@tomas.lang: Co se testování týče, nemusí se na toho obra až tak složitě. :) Není zapotřebí mockovat přímo dibi driver.

Všimni si, že entity na nízké úrovni pracují pouze s Row. Connection je v něm velmi důsledně zapouzdřené. A proto stačí vytvořit mock (stub) pouze pro Row.

Zde je malá ochutnávka:

/**
 * @property int $id
 * @property string $name
 * @property Book[] $books m:belongsToMany
 */
class Author extends Entity
{
}

/**
 * @property int $id
 * @property string $name m:passThru(emphase)
 */
class Book extends Entity
{

    protected function emphase($value)
    {
        return strtoupper($value);
    }

}

class RowMock extends Row
{

    private $mapper;

    private $data;


    public function __construct(IMapper $mapper, array $data = array())
    {
        $this->mapper = $mapper;
        $this->data = $data;
    }

    public function isDetached()
    {
        return false;
    }

    public function getMapper()
    {
        return $this->mapper;
    }

    public function setConnection(Connection $connection)
    {
    }

    public function hasConnection()
    {
        return true;
    }

    public function __set($name, $value)
    {
        $this->data[$name] = $value;
    }

    public function __get($name)
    {
        return $this->data[$name];
    }

    public function referencing($table, $viaColumn = null, Filtering $filtering = null, $strategy = null)
    {
        if ($table === 'book') {
            $rows = [];
            $data = [
                ['id' => 1, 'name' => 'First book'],
                ['id' => 2, 'name' => 'Second book'],
            ];
            foreach ($data as $entry) {
                $rows[] = new RowMock($this->mapper, $entry);
            }
            return $rows;
        }
    }

}

////////////////////
////////////////////

$connection = new Connection([ // fake!
    'lazy' => true,
]);

$author = new Author(new RowMock($mapper));
$author->makeAlive($entityFactory, $connection, $mapper);

$author->name = 'John Doe';

echo $author->name, "\n";

foreach ($author->books as $book) {
    echo "- $book->name\n";
}

Ukázka vypíše následující:

John Doe
- FIRST BOOK
- SECOND BOOK

Je to takhle pro Tebe použitelné? Anebo by sis to představoval ještě nějak lepší?

Editoval Tharos (11. 3. 2014 0:14)

před 5 lety

duke
Člen | 637

Narazil jsem na problém při persistování entity, kde používám passThru.

Mám následující entitu:

/**
 * @property int         $id
 * @property Person|NULL $person m:hasOne
 * @property string      $username
 * @property string      $password
 * @property array       $roles m:passThru(decodeRoles|encodeRoles)
 * @property bool        $active
 * @property \DateTime   $created
 */
class User extends \LeanMapper\Entity
{
    /**
     * @param  string
     * @return string[]
     */
    public function decodeRoles($roles)
    {
        return explode(',', $roles);
    }

    /**
     * @param  string[]
     * @return string
     */
    public function encodeRoles(array $roles)
    {
        sort($roles);
        return implode(',', $roles);
    }

Potom při následujícím kódu:

$user = new Entity\User;
$user->assign(array(
    'username' => 'admin',
    'password' => Passwords::hash('test'),
    'roles' => array('admin'),
    'person' => NULL
));
$userRepository->persist($user);

… dostávám výjimku se zprávou:
Error has occured (SQLSTATE[42S22]: Unknown column ‚admin‘ in ‚field list‘)1054

přičemž vygenerované SQL query bylo dle logovacího panelu:

INSERT INTO `user` (`person_id`, `username`, `password`, `roles`)
VALUES (NULL, 'admin', '$2y$10$aamqetHX3YCg4COzR/u4WeIhTQBfce9Ilv/5JKvKKtw7yVYH3RAvO', admin)

Když však změním řadek:

* @property array     $roles m:passThru(decodeRoles|encodeRoles)

za:

* @property string[]  $roles m:passThru(decodeRoles|encodeRoles)

… dostávám výjimku LeanMapper\Exception\InvalidValueException se zprávou:
Unexpected value type given in property ‚roles‘ in entity Model\Entity\User, array of string expected, string given.

A konečně, když použiji řádek:

* @property string    $roles m:passThru(decodeRoles|encodeRoles)

… kód proběhne bez chyb a uživatel se persistuje. Avšak nyní při přístupu k $user->roles dostávám notice:
Array to string conversion (File: …/vendor/tharos/leanmapper/LeanMapper/Entity.php:171)

Jak to má tedy být? Resp. co dělám špatně?
Prostě jen chci do databáze ukládat role jako řetězec a v entitách s nimi pracovat jako s polem…

před 5 lety

Šaman
Člen | 2275

Střelba od boku – zkoušel jsi typ array|string?

před 5 lety

Tharos
Člen | 1042

@duke: Tohle mi zavání nějakým bugem, zvláštní je ten úplně první SQL dotaz, který píšeš (s admin bez uvozovek). Jak budu mít chvilku, ověřím to (a vyřeším, pokud to bude bug).

před 5 lety

duke
Člen | 637

@Šaman Ne, to opravdu nefunguje. Takovéto míchání typů v anotaci LeanMapper vůbec nepodporuje.

Zatím jsem to vyřešil tak, že jsem tam nechal typ string[] a zakomentoval tu kontrolu v Entity.php, která při __set vyhazuje výjimku, neboť zjevně se tam nebere v potaz to, že passThru setter může změnit hodnotu z deklarovaného pole na skalár.

před 5 lety

Tharos
Člen | 1042

@duke: Skutečně to byla chyba. Typ přiřazované hodnoty se kontroloval na nízké úrovni i při použití passThru, což je samozřejmě nežádoucí.

Mohl bys prosím ověřit verzi dev-hotfix/passThru? Kdybys nejel přes Composer, pushnul jsem tu větev i na GitHub…

Díky za perfektní report!

Editoval Tharos (13. 3. 2014 0:11)

před 5 lety

Tharos
Člen | 1042

Ahoj přátelé,

tak dokumentační Wiki je plně připravená.

Kdo tedy máte chuť přiložit ruku k dílu, vítejte na palubě. :) Sám se pustím do migrace obsahů, které mám rozpracované v tomto repositáři a také u sebe na disku.

Případní přispěvovatelé, určitě nepřehlédněte stránku o tom, jak přispívat.

Díky za pozornost a spolupráci!

před 5 lety

janpecha
Člen | 58

@Tharos: použít github wiki je dobrý nápad, hned jak budu mít trochu času, zkusím přispět.

před 5 lety

duke
Člen | 637

Tharos napsal:

@duke: Skutečně to byla chyba. Typ přiřazované hodnoty se kontroloval na nízké úrovni i při použití passThru, což je samozřejmě nežádoucí.

Mohl bys prosím ověřit verzi dev-hotfix/passThru? Kdybys nejel přes Composer, pushnul jsem tu větev i na GitHub…

Tak jsem to otestoval a pokud zvolím anotaci:

* @property array     $roles m:passThru(decodeRoles|encodeRoles)

… tak to, zdá se, funguje dobře.

Pokud zvolím typ string[] nebo string, chová se to stejně jako před tím. Tj. pro typ string[] to stále kontroluje typ přiřazené hodnoty na nízké urovni i při použití passThru a vyhodí výjimku.
Mám za to, že anotace s typem string[] by měla fungovat…

před 5 lety

David Ďurika
Člen | 341

Tharos napsal(a):
Případní přispěvovatelé, určitě nepřehlédněte stránku o tom, jak přispívat.

ahoj ak ti nevadi ze by som to pisal po slovesnky, tak by som ti s tym rad pomohol… dokonca som uz aj zacal s IMapper-om

a dalsia vec, tie diskusne stranky k sa motnym strankam mi pridu trocha naprakticke… (vid moj koment https://github.com/…ivat-diskuze)

před 5 lety

Tharos
Člen | 1042

@duke: Aktuálně Lean Mapper zápisy typu string[] nepodporuje. [] lze použít pouze u objektů.

Já osobně to u skalárních hodnot totiž považuji tak trochu za zbytné. Hlavní výhodu zápisu Book[] $books vidím v tom, že kdy pak iteruji nad $books, IDE mi pro každou knihu napovídá její property. Z tohohle pohledu zápis string[] nemá žádný přínos oproti array.

No a co se validace dat týče… Ona už teď není kdo ví jak důkladná. Mám dojem, že se kontroluje jenom první hodnota. Přínos je spíš takový, že hned zjistíš, když vytvoříš kolekci nějakých úplně špatných typů. Kontrolovat úplně všechny hodnoty se mi nechce z výkonnostního hlediska… U velkých kolekcí se každý průchod počítá.


Takže ten fix z mého pohledu funguje. U passThru se předpokládá, že type hint bude takový, jaká bude hodnota v PHP. V Tvém případě tedy array.

před 5 lety

Tharos
Člen | 1042

achtan napsal(a):

ahoj ak ti nevadi ze by som to pisal po slovesnky, tak by som ti s tym rad pomohol… dokonca som uz aj zacal s IMapper-om

Vůbec nevadí. :)

a dalsia vec, tie diskusne stranky k sa motnym strankam mi pridu trocha naprakticke… (vid moj koment https://github.com/…ivat-diskuze)

Budou to issues. :) Jak budu mít chvilku, Wiki upravím.

Editoval Tharos (14. 3. 2014 15:21)

před 5 lety

duke
Člen | 637

Tharos napsal:

@duke: Aktuálně Lean Mapper zápisy typu string[] nepodporuje. [] lze použít pouze u objektů.

Já osobně to u skalárních hodnot totiž považuji tak trochu za zbytné. Hlavní výhodu zápisu Book[] $books vidím v tom, že kdy pak iteruji nad $books, IDE mi pro každou knihu napovídá její property. Z tohohle pohledu zápis string[] nemá žádný přínos oproti array.

Rozumím tomu, že z hlediska napovídání IDE v tom nemusí být žádný přínos. Přesto si myslím, že by to LeanMapper mohl umět (to, že to lze použít jen u objektů a u jiných typů ne, je trochu WTF). A pokud to neumí, měl by alespoň vyhazovat výjimku už při zpracování takových anotací (případně s doporučením: use „array“ as type). Výjimka, kterou to vyhazuje teď, je zavádějící.

No a co se validace dat týče… Ona už teď není kdo ví jak důkladná. Mám dojem, že se kontroluje jenom první hodnota. Přínos je spíš takový, že hned zjistíš, když vytvoříš kolekci nějakých úplně špatných typů. Kontrolovat úplně všechny hodnoty se mi nechce z výkonnostního hlediska… U velkých kolekcí se každý průchod počítá.

Jedna věc je kvantita (validace celé kolekce vs jen prvního prvku; napadá mě také možnost líné validace pokud by se pracovalo s nějakým iterátorem, či volitelná validace, kterou by šlo např. aktivovat jen ve vývojovém režimu, atp.) a druhá věc je kvalita (validují se všechny typy polí vs validují se jen pole objektů; zde nevidím důvod omezovat se jen na pole objektů).

Takže ten fix z mého pohledu funguje. U passThru se předpokládá, že type hint bude takový, jaká bude hodnota v PHP. V Tvém případě tedy array.

Pro anotaci array už opravdu funguje a za tuto rychlou opravu děkuji.

před 5 lety

Šaman
Člen | 2275

U toho string[] by asi lepší hláška pomohla, ale podpora tohoto zápisu imho nemá v php význam. Jinak teď dělám v Doctrine a na základní práci mi přijde LM dokonce jednodušší (v LM stačí přidat anotaci, v Doctrině property a setter/getter). Jediné, co tomu chybí a čím Doctrine trumfuje je aktualizace databáze podle aktuáního kódu (čímž odpadá nutnost vytvářet sql patche a řešit který má být dřív… při práci více vývojářů na jednom projektu to je k nezaplacení).

před 5 lety

castamir
Člen | 631

@Šaman to už se tu někde řešilo… DBAL z docrine se dá použít i v LM…

Nedávno jsem narazil na jeden blogpost o přechodu z NDBT na Doctrine a docela jsem se zhrozil z podoby entity. Krom tvých nedostatků bych přidal ještě nutnost duplikovat datový typ v anotaci…

před 5 lety

Tharos
Člen | 1042

elden46 napsal(a):

Zdravim,

potreboval bych natuknout smer, jak resit nasledujici:
Mam 3 entity: dilo, verze, zaznam
Dilo je zakladni, z nej vazba hasmany do verze, z nej vazba hasmany do zaznam.

Priklad: Dilo je „Pan Prstenu“ (autor, datum vydani), verze je „kniha/film/komiks“ (nakladatelstvi, id. cislo), zaznam je „ABC“ (ev. cislo, fyzicke umisteni).

Vytvarim vyhledavaci formular, do ktereho si uzivatel muze zadat vyhledavaci parametry ze vsech tri entit a dostane seznam „zaznamu“ vyfiltrovanych podle zadaneho nastaveni formulare. Jak na to, abych co nejefektivneji vyuzil LM a DB? Udelat si filtrovani nad „Dilo“ a pote si pres foreach na „verzi“ a nasledne foreach na „zaznam“ upravovat vyslednou kolekci (unsetovat „dilo“) a pak vypisovat ze zbytku? Jak pak zajistit treba sortovani podle sloupcu ze „zaznam“?
Nebo si napsat dotaz na urovni dibi a prijit tak o vyhody orm?

Odpovídám pozdě, asi už budeš mít tuhle věc vyřešenou…

Napsáním vlastního dotazu na úrovni dibi nemusíš přijít o výhody ORM. Napsal bych si na to buďto metodu v repositáři, anebo nějaký Query Object. Stačí provést SELECT potřebných dat a v restrikci při-joinovat hodnoty ze zbylých tabulek, podle kterých se má výsledek omezovat. A pak to jen celé obalit voláním ->createEntites(...) v repositáři.

Výše uvedeným způsobem snadno libovolně omezíš záznamy v rámci načítání „hlavních entit“, a to i na základě dat z jiných tabulek. Pokud bys dále potřeboval omezit data při traverzování, můžeš si nadefinovat potřebné filtry. Ty mohou jako parametr přijímat například stejný Query Object, jaký byl použit pro získání dat z hlavní tabulky.

Je to takhle „zhutněle“ vysvětlené srozumitelné? :) Pokud ne a zajímalo by Tě to víc, klidně se mi ozvi na Skype (v.kohout).

před 5 lety

Šaman
Člen | 2275

Díky, tou dobou jsem ještě netušil, že se pustím do většího projektu na Doctrině a tyhle příspěvky jsem přeskakoval. :)

před 5 lety

Tharos
Člen | 1042

Ahoj,

mile rád bych odlehčil tomuhle dloouhému vláknu a napadlo mě, že bychom mohli významnou část diskuze dále vést na GitHubu v issues.

Issues, jak asi nikoho nepřekvapí, nejsou jenom o chybách. Vytvořil jsem následující sadu štítků:

  • Approwed feature
  • Bug
  • Duplicate
  • Question
  • RFC
  • Wiki

Když je budeme používat, tak by mělo být možné na GitHubu řešit vše mnohem přehledněji – od dotazů přes RFCčka až po bugy.

Vím, že j zapojení se je zapotřebí mít účet na GitHubu, ale to je myslím malicherná vstupní bariéra.

Díky za pozornost. :)

Editoval Tharos (16. 3. 2014 21:56)

před 5 lety

Pavel Macháň
Člen | 285

Možná(asi určitě) se to tu už řešilo ale najít to v 1k postech je to dost šílený najít. Jak by jste řešili následující:
https://www.dropbox.com/…pi/LM-02.png

- Foo > id: 1, parent: null
  |- FooFoo > id: 2, parent: 1
  |- FooTwo > id: 3, parent: 1
     |- FooTwoOne > id: 4, parent: 3
- Bar > id: 5, parent: null
  |- BarBar > id: 6, parent: 5

Položky:

One > kategorie: 1
Two > kategorie: 4
Three > kategorie: 3
Four > kategorie: 2
Five > kategorie: 4

A našim cílem je pro každou kategorii vypsat své vlastní položky + ty které vlastní i jeji potomci.

Výsledná data:

Foo > id: 1 => One, Two, Three, Four, Five
FooFoo > id: 2 => Four
FooTwo > id: 3 => Two, Three, Five

Editoval EIFEL (17. 3. 2014 18:03)

před 5 lety

castamir
Člen | 631

@EIFEL jedním z mnoha způsobů (vím o 4), jak to řešit, je použití closure tables. Můj blog, kde popisuju jejich použití, je sice teď vypnutý, ale alespoň odkážu na podobný článek: http://blog.voracek.net/…rochu-jinak/

Předem však upozorňuji na drobné nedostatky v uvedených sql – ověř si, zda doopravdy dělají to, co chceš.

před 5 lety

Pavel Macháň
Člen | 285

castamir napsal(a):

@EIFEL jedním z mnoha způsobů (vím o 4), jak to řešit, je použití closure tables. Můj blog, kde popisuju jejich použití, je sice teď vypnutý, ale alespoň odkážu na podobný článek: http://blog.voracek.net/…rochu-jinak/

Předem však upozorňuji na drobné nedostatky v uvedených sql – ověř si, zda doopravdy dělají to, co chceš.

@castamir Díky mrknu na to :) Closure table sem neznal.

před 5 lety

greeny
Člen | 406

Ahoj, mám menší dotaz:

Pokud mám anotaci m:belongsToMany, dá se nějak upravit pořadí, v jakém se vrátí ty entity? Dá se to nějak udělat na úrovni SQL nebo musím pomocí m:passThru prohodit to pole nějakým usortem?

Editoval greeny (25. 3. 2014 9:54)

před 5 lety

Tharos
Člen | 1042

@greeny: Dá úplně snadno, stačí použít filtr, který do toho dotazu doplní potřebný ORDER BY.

Jinak koukám, že máš účet na GitHub – kdybys cokoliv potřeboval, ptej se prosím přednostně v Lean Mapper issue. Stačí vytvořit issue a dát ji štítek question. Díky!

před 5 lety

greeny
Člen | 406

@Tharos – jo, já si totiž moc nečetl ty posty nad tím, všiml jsem si toho až po odeslání dotazu ;) Díky

Editoval greeny (25. 3. 2014 11:41)

před 5 lety

johnson
Člen | 1

Ahoj, prosím o radu. Chtěl bych nějak řešit unikátnost záznamů při persistenci. Příkládám příklad, na kterém se to pokusím vysvětlit. Entity a repositáře vychází z quickstartu k leanmapperu.

<?php

$tag = new Tag();
$tag->name = "Horror";

$tagRepository->persist($tag);

$book = new Book();
//zde by bylo přiřazení povinných hodnot

$book->addToTags($tag);
$bookRepository->persist($book);

?>

V TagRepository bych chtěl přetížit metodu persist a v ní zkontrolovat zda tag se zadaným name již existuje. Pokud ano, tak nezakládat nový a předanou entitu tagu označit jako existující a přiřadit ji id záznamu v db. Čili potom při volání $book->addToTags($tag) bude kniha propojena s již existujícím tagem. Doufám, že jsem to popsal srozumitelně.

Poradíte jak na to prosím? Co by mělo být obsahem přetížené metody persist a zda to vůbec takto řešit?

Editoval johnson (31. 3. 2014 13:06)

před 5 lety

Tharos
Člen | 1042

@johnson: Mělo by to jít velmi jednoduše. V repositáři se stačí někde před vytvořením entity podívat do databáze, jestli už taková entita existuje, a pokud ano, můžeš tu do repositáře předanou na tu existující „napojit“ pomocí metody Entity::attach.

Asi bych pak ještě zkontroloval, jestli v té předané nebyly pozměněny nějaké hodnoty, které bych kdyžtak v úložišti také aktualizoval…

Anebo by mělo jít použít v repositáři konstrukci INSERT ... ON DUPLICATE KEY UPDATE. To by bylo asi ještě lepší, čistější řešení. :)

Stačí Ti takhle rámcové navedení? Pokud ne, můžeme to probrat ještě na GitHubu v issues. Díky!

před 5 lety

plzurq
Člen | 3

Ahoj,

jde nějak elegantně vyřešit následující?

Již mám article, který má nějaké tagy (např 1,2,3). Updatuju ho a ukládám a tagy upravím (a vyberu 2,3,4)

Chtěl bych, aby se mi při ukládání smazala 1 a přidala 4.

$tags=[2,3,4];
$article = //vytahnu z DB article
$article->addToTags($tags);
$this->repo->persist($article);

Editoval plzurq (7. 4. 2014 21:40)

Stránky: Prev 1 … 19 20 21 22 23 Next