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 6 lety

kudlajz
Člen | 70

@Tharos:

Nikdy si neresil cachovani? Jde mi o to proto, ze web bude navstevovat spousta lidi a bez cache by to ten server urcite zabilo, ne? :)

Prave premyslim, jestli ma nakonec smysl vubec pouzit ORM nebo jestli si napisu vlastni modely ciste v dibi..

před 6 lety

Šaman
Člen | 2275

Kešování se dá řešit i jen na úrovni šablon, takže bys nekešoval dotazy, ale až výsledná a už zpracovaná data.
Co je spousta lidí? Kolik za minutu a očekáváš nějaké koncentrace v určitých časech? (Např. sázkový systém je extrémně vytížený hodinu po skončení zápasu, tam má smysl kešovat hodně.)

před 6 lety

kudlajz
Člen | 70

Pokud bych cachoval na urovni sablon, jak je to potom s invalidaci dat? A pokud plnim template v metode render, pri cachovani v sablone se nebude volat dotaz na data stale znovu? Jde o socialni sit, s moznosti realtime konverzaci atp., Facebook-like projekt, jen s mensi navstevnosti.

EDIT: I tak mi prijde cachovat jako dobry napad, kdyz jde o data, ktera se nemeni, nebo se meni jen velmi malo, tak mi prijde zbytecne pri kazdem requestu je tahat z DB, kdyz to muzu nacist z disku, jeste navic, kdyz ma VPS jede na SSD.

Editoval kudlajz (24. 12. 2013 11:12)

před 6 lety

sKopheK
Člen | 207

Invalidaci dat udelas klasicky pres tagy: https://doc.nette.org/cs/2.4/caching#…

$templateCache = \Nette\Environment::getCache('Nette.Template.Cache');
$templateCache->clean(array(Cache::TAGS => $tags));

před 6 lety

kudlajz
Člen | 70

Cachovani v sablone ale asi neni uplne idealni.
Uvedme priklad – mejme metodu findUser($id), ktera fetchne z DB uzivatele dle id a ulozi do cache, pri pristim volani teto metody, pokud je cache s timto id validni, tak se pouzije cache, jinak se provede opet krok c. 1.

Pokud to cachuju v te metode napr. v presenteru, tak je to ok a kdyz se ta metoda bude volat treba 10× behem vykonani skriptu, tak se to nacte z DB jednou a z cache 9×.

Pokud ale budu chachovat na urovni sablon, tak se ten vysledek bude cachovat jen v te urcite sablone a vsechna ostatni pouziti nebudou ovlivnena.

před 6 lety

Mesiah
Člen | 242

kudlajz napsal(a):

pokud ti jen o to snížit počet dotazů do db tak si teoreticky takovou cache můžeš napsat sám, v podstatě jen potřebuješ nějakou Dictionary, kde klíč může být složenina názevEntity-ID a hodnota samotná entita. Pokud entitu v dictionary nenajdeš, tak ji dočteš a vložíš.
Zajímavější ale budou dotazy kde je klauzuje where whatever in (1, 2, 3) tady je už je více strategií jak se zachovat. :)

Editoval Mesiah (27. 12. 2013 18:42)

před 6 lety

jsvelta
Člen | 39

Ahoj, mám jeden problém. Rieším to už zopár hodín.
Mám entitu:

/**
 * @property-read int $id
 * @property Paper $paper m:hasOne
 * @property User $reviewer|NULL m:hasOne(reviewer_id)
 * @property int $originality|NULL
 * @property int $theme|NULL
 * @property int $technicalquality|NULL
 * @property int $linguisticquality|NULL
 * @property int $recommendation|NULL
 * @property string $note|NULL
 */
class Review extends Entity {
}

v latte niečo takéto:

{foreach $papers as $paper}
    {foreach $paper->reviews as $review}
        {dump $review->reviewer}
    {/foreach}
{/foreach}

Ako v latte detekovať, či má review vyplnené reviewer?
Nijak sa mi to nedarí. Čakal som, že $review->reviewer bude NULL, ale to nefunguje, dokonca to padá na výnimku.

Editoval jsvelta (28. 12. 2013 23:21)

před 6 lety

Šaman
Člen | 2275

Změň to to z: @property User $reviewer|NULL m:hasOne(reviewer_id)
na tohle: @property User|NULL $reviewer m:hasOne(reviewer_id)

Null je typ (Objekt uživatel, nebo null), ne název. To samé máš u těch intů, ale tam ti to nespadne na výjimku, jen se přetypuje na 0.

před 6 lety

jsvelta
Člen | 39

Šaman napsal(a):

Změň to to z: @property User $reviewer|NULL m:hasOne(reviewer_id)
na tohle: @property User|NULL $reviewer m:hasOne(reviewer_id)

Ďakujem, funguje to.
Prehliadol som, že to dávam úplne inam, než chcem :-)

před 6 lety

Michal III
Člen | 84

Ahoj, pokusil jsem napsat „něco jako Query Object“, který by mi vyhovoval při práci s repozitáři. Zajímaly by mě vaše názory a podněty.

LeanMapperQuery (GitHub)

Je to inspirováno „backjoin“ z NDB (NotORM). Přistoupím k ukázkám použití:

Mějme následující následující repozitáře (schéma databáze převzato z quickstartu LeanMapperu):

abstract class BaseRepository extends \LeanMapper\Repository implements IQueryable
{

    /**
     * @implements IQueryable
     */
    public function createFluent()
    {
        return parent::createFluent();
    }

    /**
     * @implements IQueryable
     */
    public function getTable()
    {
        return parent::getTable();
    }

    public function query()
    {
        return new BaseQuery($this, $this->mapper);
    }

    public function find(IQuery $query)
    {
        return $this->createEntities($query->createQuery());
    }

}

class AuthorRepository extends BaseRepository
{

}

class BookRepository extends BaseRepository
{

}

a entity:

class BaseEntity extends \LeanMapper\Entity
{

}

/**
 * @property int $id
 * @property string $name
 */
class Tag extends BaseEntity
{
}

/**
 * @property int $id
 * @property string $name
 */
abstract class Person extends BaseEntity
{
}

/**
 * @property Book[] $books m:belongsToMany
 * @property Book[] $reviewedBooks m:belongsToMany(reviewer_id)
 * @property string|null $web
 */
class Author extends Person
{

}

/**
 * @property int $id
 * @property Author $author m:hasOne
 * @property Author|null $reviewer m:hasOne(reviewer_id)
 * @property Borrowing[] $borrowings m:belongsToMany
 * @property Tag[] $tags m:hasMany
 * @property string $pubdate
 * @property string $name
 * @property string|null $description
 * @property string|null $website
 * @property bool $available
 */
class Book extends BaseEntity

}

Dále si nadefinujeme BaseQuery objekt:

class BaseQuery extends Query
{
    public function find()
    {
        return $this->sourceRepository->find($this);
    }
}

Pro znázornění spolupráce s implicitními filtry ještě uvedu Mapper:

class Mapper extends DefaultMapper
{
    public function getImplicitFilters($entityClass, Caller $caller = NULL)
    {
        if ($entityClass === "$this->defaultEntityNamespace\\Book") {
            return new \LeanMapper\ImplicitFilters(function (\LeanMapper\Fluent $statement) {
                $statement->where('[available] = 1');
            });
        }
        return array();
    }
}

Poté můžeme pracovat s repozitáři následujícím způsobem:

$bookRepository = new Repository\BookRepository($connection, $mapper, $entityFactory);

$books = $bookRepository->query()
    ->where('@tags.name', 'popular')
    ->where('DATE(@pubdate) > ?', '1998-01-01')
    ->find();

Veškeré @... výrazy se překládají na sloupce příslušných entity a tečková notace slouží k automatickému „left-joinování“. Položený dotaz potom vypadá takto:

SELECT [book].*
FROM [book]
LEFT JOIN [book_tag] ON [book].[id] = [book_tag].[book_id]
LEFT JOIN [tag] ON [book_tag].[tag_id] = [tag].[id]
WHERE [available] = 1 AND ([tag].[name] = 'popular') AND (DATE([book].[pubdate]) > '1998-01-01')

Pro backjoin se tedy používá pouze tečka (netřeba dvojtečky), protože příslušná vazba se vyčte ze třídy entity. Jak je vidět v dotazu, implicitní filtry se provedou.

Ukázka trochu komplikovanějšího dotazu:

$authorRepository = new Repository\AuthorRepository($connection, $mapper, $entityFactory);

$authors = $authorRepository->query()
    ->where('@books.tags.name', 'ebook')
    ->orderBy('@books.available')->desc()
    ->orderBy('@books.pubdate')
    ->find();
SELECT [author].*
FROM [author]
LEFT JOIN (
SELECT [book].*
FROM [book]
WHERE [available] = 1) book ON [author].[id] = [book].[author_id]
LEFT JOIN [book_tag] ON [book].[id] = [book_tag].[book_id]
LEFT JOIN [tag] ON [book_tag].[tag_id] = [tag].[id]
WHERE ([tag].[name] = 'ebook')
ORDER BY [book].[available] DESC , [book].[pubdate]

Pokud se má tedy „přijoinovat“ tabulka, na které se mají provést implicitní filtry, provede se složený dotaz, jak je vidět u joinování tabulky book.


Celý projekt je zatím ve fázi experimentování, proto budu vděčný za veškeré názory, podněty a bug reporty. Zajímá mě také kritika tohoto pohledu na Query Objecty, protože si sám nejsem zatím jistý, jestli to až příliš neporušuje některé principy, nicméně z mých dosavadních zkušeností mohu říct, že se mi takto pracuje s repozitáři zdaleka nejlépe ze všech mnou zatím vyzkoušených možností.

Editoval Michal III (29. 1. 2014 10:50)

před 6 lety

Tharos
Člen | 1042

kudlajz napsal(a):

@Tharos:

Nikdy si neresil cachovani? Jde mi o to proto, ze web bude navstevovat spousta lidi a bez cache by to ten server urcite zabilo, ne? :)

Prave premyslim, jestli ma nakonec smysl vubec pouzit ORM nebo jestli si napisu vlastni modely ciste v dibi..

Jsem zastáncem myšlenky, že řešit cache má smysl až v momentě, kdy se ukáže, že je to skutečně zapotřebí. A také považuji za důležité měřením ověřit, že zamýšlené řešení je skutečně funkční. Máš změřené, že se data skutečně načtou rychleji z cache než z databáze? (Samozřejmě v produkčním prostředí.)

Říct si na začátku vývoje, že tohle a tamto asi bude nutné cacheovat a rovnou nějakou cache naimplementovat, považuji za premature optimization jak z učebnice.

K Tvému konkrétnímu dotazu:

  • Víš určitě, že si s tím server neporadí?
  • Pokud ne, víš určitě, že aplikace je nejlepší místo, kde tu cache řešit? Nebylo by lepší spíše mít nějakou cache na úrovni databáze? Nebo memcache? Nebo optimalizovat dotazy? Či minimalizovat délku round tripu do databáze?

Nemáš-li zdopovězené tyto dotazy, může se Ti klidně stát, že naimplementuješ nějakou cache, která může být „suboptimální“, anebo v nejhorším případě může být pomalejší, než načítání dat z úložiště…

Editoval Tharos (30. 12. 2013 0:14)

před 6 lety

Jan Suchánek
Backer | 403

@Tharos: Určitě cache na začátku vývoje a provozu aplikace nemá smysl řešit, ale pro testovaní možností zrychlení při zátěži by se nějaký ten příklad použítí asi mohl hodil nebo ne?

Je možné že Nette\Database je v posledním RC4 o hodně rychlejší než Dibi?

Je něco nového s dokumentací?

před 6 lety

kudlajz
Člen | 70

Muze mi nekdo poradit, jak jednoduse vlozit relaci pri m:n asociaci?
Mam v entite definovano:

@property Like[] $likes m:hasMany(post_id:post_like:like_id:like)

a zkousel jsem neco jako

$this->likes[] = new Like;

ale to nefunguje, muze mi nekdo poradit, jak na to? :)

Editoval kudlajz (4. 1. 2014 13:17)

před 6 lety

Michal III
Člen | 84

@kudlajz: Existují magické metody addTo<Name>, removeFrom<Name> (viz zde).

Takže ve Tvém případě:

$this->addToLikes(new Like);

před 6 lety

kudlajz
Člen | 70

Dekuji :)

před 6 lety

kudlajz
Člen | 70

Muze mi jeste nekdo poradit bliz s tim cachovanim? Jelikoz nelze serializovat entitu, jak mam postupovat? Radek se z db fetchuje pred volanim createEntity().

před 6 lety

Michal III
Člen | 84

@kudlajz: Nestačila by místo serializování entity její metoda getData()?

před 6 lety

Pavel Macháň
Člen | 285

kudlajz napsal(a):

Muze mi jeste nekdo poradit bliz s tim cachovanim? Jelikoz nelze serializovat entitu, jak mam postupovat? Radek se z db fetchuje pred volanim createEntity().

Cachovat data co dostaneš přímo z databáze před vytvořením entity.
Cache, pokud není tak load z DB > createEntity

Nebo jsem cachoval celé komponenty (v render jsem bud nahral nové data z DB a klasicky vykreslil a nebo sem si načetl z cache celé html co jsem si do ní narval a vykreslil jej)

Editoval EIFEL (7. 1. 2014 21:08)

před 6 lety

Mesiah
Člen | 242

Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve stylu filter/passThru? Díky :)

před 6 lety

David Grudl
Nette Core | 6806

Nebylo by lepší, kdybych celé toto vlákno přesunul na dibi fórum?

před 6 lety

Tharos
Člen | 1042

Na dibi fóru by to byl podobný offtopic jako tady, protože téměř 100 % dotazů nesouvisí přímo s dibi, ale s onou nadstavbou nad ní…

Během několika týdnů by měl mít projekt své vlastní fórum, a tak prosím ještě o chvilku strpení… Pak si klidně všechny zdejší příspěvky přemigruji.

Do té doby už rozhodně nevyjde žádná další verze, takže předpokládám, že tohle vlákno už nebude ani zdaleka tak živé, jako bylo ještě nedávno.

Díky.

Editoval Tharos (8. 1. 2014 0:02)

před 6 lety

tomas.lang
Člen | 54

Mesiah napsal(a):

Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve stylu filter/passThru? Díky :)

Nestačili by ti na to normální metody ve stylu:

/**
 * @property string $name
 * @property string $surname
 * @property-read string $fullName
 */
class User extends \LeanMapper\Entity
{

  public function getFullName()
  {
    return $this->name . ' ' . $this->surname;
  }

}

před 6 lety

Mesiah
Člen | 242

tomas.lang napsal(a):

Mesiah napsal(a):

Ahoj, prosím Vás jen drobnost.
Rád bych měl u entity property, kterou budu vytvářet na straně PHP (url vytvořená na základě hodnot aktuální entity), existuje v LM řešení ve stylu filter/passThru? Díky :)

Nestačili by ti na to normální metody ve stylu:

/**
 * @property string $name
 * @property string $surname
 * @property-read string $fullName
 */
class User extends \LeanMapper\Entity
{

  public function getFullName()
  {
    return $this->name . ' ' . $this->surname;
  }

}

Právě že ne. Jak jsem zmínil, tak bych chtěl generovat URL a to takovým způsobem, že i když změním v budoucnu routy, tak budu dostávat URL pro tyhle nové routy, tolik k myšlence.
Říkal jsem si, že by se mi to líbilo takhle: budu mít službu, která mi generuje URL. V LM jsou filtr, což v podstatě může být normální třída, takže teoreticky i služba a pokud by LM měl podporu pro volání metod pro sestavení nějaké složené property – podobně jako passThru nebo filters, pak by mi nic nebránilo zavolat tu svou službu na generování URL a vygenerovat URL pro každou entitu.

před 6 lety

Tharos
Člen | 1042

@Mesiah: Jako optimální mi v takovém případě připadá mít službu pro generování odkazů (jak píšeš) a tu prostě předávat dané entitě jako závislost ve vlastní EntityFactory. V property (realizované pomocí metody) bys pak o tu URL žádal tu službu.

O pár stránek dříve (v době, kdy jsem představoval právě EntityFactory) najdeš pár ukázek, od kterých se dá dobře odrazit.

Kdybys tápal, klidně se ozvi přes soukromou zprávu.

Editoval Tharos (7. 1. 2014 23:58)

před 6 lety

Mesiah
Člen | 242

Tharos napsal(a):

@Mesiah: Jako optimální mi v takovém případě připadá mít službu pro generování odkazů (jak píšeš) a tu prostě předávat dané entitě jako závislost ve vlastní EntityFactory. V property (realizované pomocí metody) bys pak o tu URL žádal tu službu.

O pár stránek dříve (v době, kdy jsem představoval právě EntityFactory) najdeš pár ukázek, od kterých se dá dobře odrazit.

Kdybys tápal, klidně se ozvi přes soukromou zprávu.

Tak to je mazec! Tohle je přesně co jsem potřeboval, skvělá skvělá práce! Mockrát díky! :)

před 6 lety

Pavel Macháň
Člen | 285

@Tharos Nějaké nové info ohledně dokumentace ? :)

Editoval EIFEL (13. 1. 2014 17:46)

před 6 lety

Tharos
Člen | 1042

Michal III napsal(a):

Ahoj, pokusil jsem napsat „něco jako Query Object“, který by mi vyhovoval při práci s repozitáři. Zajímaly by mě vaše názory a podněty.

Konečně jsem se dostal k tomu projít si to. Neprocházel jsem „vnitřnosti“ úplně do detailu, ale jako celek se mi to osobně velmi líbí!

Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.

Nechystáš se na Poslední sobotu?

před 6 lety

Michal III
Člen | 84

@Tharos:

LQL mě také v souvislosti s tím napadlo. Je to sice věc, které by se asi spousta lidí bránila, že se „nechtějí učit další jazyk“, ale na spoustu případů by se velice hodila.


Na Poslední sobotu se právě vůbec poprvé chystám :-).

před 6 lety

Tharos
Člen | 1042

@Michal III: Fajn, tak to se uvidíme. :) Pokud budeš chtít, můžeme porovnat různé přístupy. Rád Ti předvedu, co v poslední době ponejvíc používám já.

před 6 lety

Michal III
Člen | 84

@Tharos: Jj, rád s Tebou tyhle věci proberu. Těším se :-).

před 6 lety

blur
Člen | 15

Jak pomocí mapperu dokážu automaticky řadit Modely do různých namespace? Zkoušel jsem pro každý repository vytvořit v konstruktoru mapper ale to mi vynadá, že nejsou totožné. Modely mám zvlášť pro každý modul a každá tabulka má v názvu jeho prefix. S tím zase souvisí odstranění anotace entity. Jak bez ní můžu repository nastavit Název entity kupříkladu tabulka security_users ⇒ User, tabulka content_articles ⇒ Article.

<?php
class AutoMapper extends DefaultMapper
{
    public function __construct($namespace)
    {
        $this->defaultEntityNamespace = $namespace;
    }
}
?>

před 6 lety

honos
Člen | 109

Tharos napsal(a):

Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.

Sice LM nepoužívám ale také by mě zajímalo co chystáš.. A také na kdy tak vidíš aktualizaci dokumentace pro verzi 2.x.x

před 6 lety

Nobody.guy
Člen | 22

Neocenil by někdo generování struktury databáze z vlastností v anotacích?

Editoval Nobody.guy (27. 1. 2014 11:42)

před 6 lety

Pavel Macháň
Člen | 285

Nobody.guy napsal(a):

Neocenil by někdo generování struktury databáze z vlastností v anotacích?

@Tharos už něco napsaného na to měl jen to myslím nebylo dotažené aby to zveřejnil.

před 6 lety

Nobody.guy
Člen | 22

EIFEL napsal(a):
@Tharos už něco napsaného na to měl jen to myslím nebylo dotažené aby to zveřejnil.

To je škoda :)

před 6 lety

Filip111
Člen | 244

Ahoj, mám dotaz ohledně práce s M:N vazbami.
Mám entitu Schedule:

/**
* @property int $id
* @property string|null $title
* @property Classroom[] $classrooms m:hasMany(schedule:gaudeo_classrooms_schedule:classroom:gaudeo_classrooms) m:filter(orderBySorting)
*/

Jak přidám nebo odeberu prvek z $classrooms ?
Doposud mi fungovalo

$schedule->addToClassrooms($classroom);
$schedule->removeFromClassrooms($classroom);

Jenže co jsem přidal filter nad property classrooms tak to nejde použít (Chyba: ‚Only properties without filters can be managed this way.‘)

Zkoušel jsem:

$cl = $schedule->classrooms;
unset($schedule->classrooms[$classroom->id]);
unset($schedule->classrooms[$classroom]);
unset($cl[$classroom->id]);
unset($cl[$classroom]);
$cl->remove($classroom);

ale nic z toho nefunguje – vždy se dostanu k nějaké chybové hlášce.

Díky, Filip

před 6 lety

Etch
Člen | 404

@Tharos

Nešlo by přidat do konstruktoru entity volání nějaké protected metody jako je initDefaults(), ale pro případ, kdy je entita vytvářena z Row?

Občas by se mi hodilo s takto vytvořenou entitou ještě něco provést. Občas se mi například hodí si uložit kopii hodnot sloupců z Row, abych je mohl v budoucnu porovnat s getModifiedRowData() atd.

před 6 lety

Mesiah
Člen | 242

Filip111 napsal(a):

Jak přidám nebo odeberu prvek z $classrooms ?

Jenže co jsem přidal filter nad property classrooms tak to nejde použít (Chyba: ‚Only properties without filters can be managed this way.‘)

Myslím, že chybová hláška je celkem výmluvná, každopádně odpověď jak přidávat a odebírat je prostá – přes repository classroom(s). Teď by asi byla vhodná otázka: „Proč to nejde?“.
Na tohle neumím dostatečně správně a přesně odpovědět, ale řekl bych že to souvisí s filtry – to že při čtení se ke query připojí filtr je zřejmě, ale jak by se to mělo chovat při mazání?
Mělo by se to prohnat filtr? Nemělo? – Dokážu si představit oba scénáře.
Myslím, že Tharosovo rozhodnutí vyhodit chybu je na místě – tímhle by se aplikace chovala nepředvídatelně a mohlo by to vést ke špatně odhalitelným chybám – zvlášť ve vícečlenných týmech…
Víc by ti ale asi napsal Thahos. :)

Editoval Mesiah (28. 1. 2014 16:15)

před 6 lety

Filip111
Člen | 244

@Mesiash:
Jít na to z druhý strany mě nenapadlo – snad to pomůže.
Můj dotaz byl zaměřený spíš jestli existuje nějaká alternativa k použití pseudofunkcí addToClassrooms a removeFromClassrooms.

Tharos o něčem uvažoval, ale nevím co z toho by nakonec mělo fungovat (podle toho co jsem zkoušel, tak nic), viz. https://forum.dibiphp.com/…orm-nad-dibi#…

Dík.

před 6 lety

Tharos
Člen | 1042

@blur:

Jak pomocí mapperu dokážu automaticky řadit Modely do různých namespace? Zkoušel jsem pro každý repository vytvořit v konstruktoru mapper ale to mi vynadá, že nejsou totožné. Modely mám zvlášť pro každý modul a každá tabulka má v názvu jeho prefix. S tím zase souvisí odstranění anotace entity. Jak bez ní můžu repository nastavit Název entity kupříkladu tabulka security_users ⇒ User, tabulka content_articles ⇒ Article.

To, aby v celé aplikaci existoval pouze jeden Mapper, je poměrně důležité. Jde o to, že Mapper se předává při traverzování mezi entitami (úplně nenápadně) a pokud by existovalo více Mapperů, téměř by nešlo zabránit nechtěným chybám, kdy se najednou objeví entita A s Mapperem X (který získala při traverzování), namísto očekávaného Y (který by měla získat z repositáře).

Překlad security_usersUser je přece jednoduchý, nebo ne? Pokud máš pevně danou logiku, můžeš odstřihnout tu část před potržítkem a upravit velikost prvního písmena. V Mapperu se ale nemusíš omezovat pouze na takovéto operace. Pokud máš například modulární aplikaci, Mapper klidně může interně používat nějaké překladové tabulky. Proměnnou $defaultEntityNamespace vůbec nemusíš používat.

Řeší má odpověď Tvůj problém?

před 6 lety

Nobody.guy
Člen | 22

Nic na generování SQL z entit teda není a můžu se do toho pustit?

před 6 lety

Tharos
Člen | 1042

@honos:

Tharos napsal(a):

Jinak při hraní si s tím mě napadlo, že by vlastně šlo postavit nad Lean Mapperem robustní LQL.

Sice LM nepoužívám ale také by mě zajímalo co chystáš.. A také na kdy tak vidíš aktualizaci dokumentace pro verzi 2.x.x

Dokumentace má rozhodně prioritu před podobnými experimenty. LQL by vytvořit šel, neexistuje žádný principiální problém, proč by nešel. Ale pokud zbude jeho realizace na mě, nějaký pátek ještě asi světlo světa nespatří. Ale třeba Michal III má nakročeno velmi hezky.

Editoval Tharos (29. 1. 2014 9:06)

před 6 lety

Tharos
Člen | 1042

Nobody.guy napsal(a):

Nic na generování SQL z entit teda není a můžu se do toho pustit?

Nástřel generování databázového schéma z entit bych měl. Je to i docela dobře funkční, ale nedokončené.

Pokud bys měl zájem to dotáhnout nebo vytvořit něco analogického (ale dotaženého), bylo by to skvělé. Klidně se mi ozvi soukromou cestou (v.kohout, gmail) a můžu Ti poskytnout, co je již hotové, a taky Ti můžu poskytnout pár myšlenek a nápadů.

před 6 lety

Tharos
Člen | 1042

@Filip111:

Ahoj, mám dotaz ohledně práce s M:N vazbami.

API pro správu jednoduchých M:N vazeb je záměrně nedostupné u položek s filtry.

Důvod je poměrně jednoduchý. Filtr s tou vazbou může udělat dost radikální věci. Například může k vazební tabulce přijoinovat nějaká data a programátor by se pak divil, že se mu pak entity „špatně“ persistují. Nebo může zavést restrikci, pomocí které se nějaká data vypustí. Mám-li vyfiltrované takové tagy knihy, které začínají na „S“ (třeba v položce tagsStartingByS), bylo by matoucí, kdyby do takové kolekce šlo přiřadit pomocí $book->addToTags nějaká tag začínající na „R“. Problém je třeba i s jednoduchým řazením. Pokud mám tagy nějak seřazené (třeba podle abecedy), spoléhám na to a někde v aplikaci si do takové property přiřadím nějaký další tag, mohu tím to seřazení narušit. No a dalo by se dlouze pokračovat.

Z výše uvedených důvodů je tedy to M:N API u takových vazeb uměle omezené. Dalo by se říct, že zavedením filtru ty vazby přestávají být jednoduché.

Principiálně tohle dost dobře vyřešit nelze. Alternativním řešením je například spravovat ty vazby přes repositář nebo mít v entitě ještě jednu ekvivalentní položku, jen bez toho filtru. Dále by to mohlo jít vyřešit i nějak „nízkoúrovňově“ v těle entity, což by mohlo být nejelegantnější řešení.

Edit: Tak „nízkoúrovňově“ by to šlo. V Row::(addTo|removeFrom)Referencing se už filtr nekontroluje, což je logické. Takže by v pohodě šlo v entitě si metodu například addToTags přetížit a vložení umožnit – ale na vlastní nebezpečí.

Editoval Tharos (29. 1. 2014 9:27)

před 6 lety

Tharos
Člen | 1042

@Etch:

Nešlo by přidat do konstruktoru entity volání nějaké protected metody jako je initDefaults(), ale pro případ, kdy je entita vytvářena z Row?

Občas by se mi hodilo s takto vytvořenou entitou ještě něco provést. Občas se mi například hodí si uložit kopii hodnot sloupců z Row, abych je mohl v budoucnu porovnat s getModifiedRowData() atd.

Pokud by mělo jít o další prázdnou protected metodu přímo v LeanMapper\Entity, tak jí moc nakloněn nejsem. Přijde mi, že využití je dost specifické.

Co třeba udělat si na to nějakou base Entity?

abstract class Entity extends LeanMapper\Entity
{

    public function __construct($arg = null)
    {
        parent::__construct($arg);
        if ($arg instanceof LeanMapper\Row) {
            $this->touchRow($arg);
        }
    }

    protected function touchRow(LeanMapper\Row $row)
    {
    }

}

před 6 lety

Etch
Člen | 404

@Tharos

Samozřejmě jen dotaz. :D

BTW: Jak kdyby si mi koukal do zrojáků :D Mám to teď napsané velmi podobně. :D

před 6 lety

Michal III
Člen | 84

@Tharos @honos: Co se týče LQL, napadají mě 2 různá řešení:

  1. Nějaká dotažená verze toho, o co jsem se již pokoušel zde, což je jen jakýsi preprocesor nad dotazy do konkrétní databáze (testováno jen na MYSQL),
  2. nebo opravdový LQL ve vyšší vrstvě, která by se posléze překládala dle použitého typu databáze – zatím si ani nedovedu představit, kolik by to stálo práce něco takového vytvořit. Kolik času by stálo parsování a překlad jednotlivých dotazů (měly by se z toho důvodu cachovat?). Šlo by tedy o ambicióznější, ale nepoměrně náročnější projekt.

Jakou cestou se vydat?

BTW hrozně se mi líbí, co vše lze z LeanMapperu zužitkovat – v dotazu by se např. dalo místo %* parametrů využívat pouze ?, který by se poté nahradil za příslušný %* parametr dle typu property v entitě…

před 6 lety

Filip111
Člen | 244

@blur, @Tharos:
Taky jsem narazil na problém s více mappery – konkrétně jsem přetraverzoval přes pár entit, upravil získanou entitu a nepodařilo se mi ji uložit, protože repository, které ji ukládá používalo jiný mapper než ten předaný při traverzování.
Dost mi to zkomplikovalo situaci.

Jak jsem se k tomu dostal?
Základem všech aplikací co dělám je zjednodušené CMS, která samo o sobě poskytuje některé „služby“ (ne nutně z kontaineru). Některé tyto „služby“ mají repository, entity a vlastní mapper.

Pak mám modul s nějakým firemním systémem, který ale používá služby cmska (práce s uživately, maily, logy, nějaká úložiště apod.). Tento modul má vlastní repository a entity a protože nechci aby o nich věděl standardní mapper cmska, mají i vlastní mapper se svými specialitami.
Entity tohoto firemního systému ale úplně klidně mohou být provázané na entity cmska (na uživatele, nějaký typové číselníky, tagy atd).

Pravidlo jediného mapperu je tedy dost svazující při snaze rozdělit aplikaci na více modulů.
(řešení v LM mě nenapadá, jen chci upozornit, že to není tak černobílé)

@Tharos, @honos, @Michal III
Vážně potřebujeme LQL a převod entit na tabulky, resp. SQL?
LeanMapper je Lean a proto ho používám, kolos který umí všechno už tu máme (Doctrine2) a přijde mi zbytečný vytvářet ho znovu.

před 6 lety

Michal III
Člen | 84

@Filip111: To je právě to, LQL nemusí být a také nejspíše nikdy nebude pevnou součástí LeanMapperu. Je to myšleno spíš jako rozšíření (nadstavba). Rozdíly mezi LeanMapperem a Doctrine nejsou jen ve „velikosti“, ale především v některých principech, každopádně se ale ani o jednom projektu nedá říct, že by byl nějak špatný nebo horší než ten druhý.

Řekl bych, že to jsou různé prostředky či „zbraně různého kalibru“, které je třeba používat na různé projekty. Někomu stačí dibi, někomu LeanMapper s repozitáři v prezentérech, někdo to požene přes fasádu a jsem si jist, že někdo by LQL či něco podobného také využil.

před 6 lety

Tharos
Člen | 1042

@Filip111:

Pravidlo jediného mapperu je tedy dost svazující při snaze rozdělit aplikaci na více modulů.
(řešení v LM mě nenapadá, jen chci upozornit, že to není tak černobílé)

Důvod, proč to tak ale je, je věřím jasný. Vůbec si nemyslím, že by to bylo nějak svazující. Jen je zapotřebí nevnímat Mapper jenom jako nějaký „tupý“ aplikátor nějakých konvencí. Může fungovat úplně dynamicky:

class DynamicMapper extends DefaultMapper
{

    private $dictionary = array();


    public function registerModule($modulePrefix, array $names)
    {
        foreach ($names as $name) {
            $this->dictionary[$modulePrefix . '_' . $name] = 'Addon\\' . ucfirst($modulePrefix) . '\\Entity\\' . ucfirst($name);
        }
    }

    public function getEntityClass($table, Row $row = null)
    {
        if (isset($this->dictionary[$table])) {
            return $this->dictionary[$table];
        }
        return parent::getEntityClass($table, $row);
    }

}

$mapper = new DynamicMapper;

$mapper->registerModule('news', array('item', 'comment', 'rating'));
$mapper->registerModule('content', array('page', 'text'));

$entityClass = $mapper->getEntityClass('news_item'); // $entityClass === 'Addon\News\Entity\Item'
$entityClass = $mapper->getEntityClass('news_rating'); // $entityClass === 'Addon\News\Entity\Rating'
$entityClass = $mapper->getEntityClass('content_page'); // $entityClass === 'Addon\Content\Entity\Page'

Pak není problém, aby si každý modul CMS při svém zavádění zaregistroval do Mapperu entity, které do „ekosystému“ přidává. Uvedená ukázka je záměrně triviální, v praxi by byl kód asi o něco složitější, ale věřím, že je z toho patrné, co chci říct.

Vážně potřebujeme LQL a převod entit na tabulky, resp. SQL?
LeanMapper je Lean a proto ho používám, kolos který umí všechno už tu máme (Doctrine2) a přijde mi zbytečný vytvářet ho znovu.

Jde o věci, které by v žádném případě nebyly nějakou nativní součástí Lean Mapperu, ale jen nadstavbou nad ním.

Generování tabulek z anotací (respektive z EntityReflection) je lákavé, protože díky existenci EntityReflection je to hračka a takové generování prostě při vývoji ušetří dost času.

Bez LQL se dá obejít. Je to prostě jeden ze způsobů, jak vyjádřit to, jaké entity a jakým způsobem se mají z úložiště načíst. Pokud by jej někdo naprogramoval, věřím, že by si své uživatele našel. Z Lean Mapperu to kolos ale určitě neudělá, do něj to nijak nezasáhne. Půda už je v podstatě připravená (EntityReflection, IMapper).

Editoval Tharos (30. 1. 2014 8:49)

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