YetORM – kvaziORM nad Nette\Database

majkl-cz
Člen | 1
+
0
-

Zdravím pánové,
v nette nedělám dlouho, ale na jednom středně velkém projektu jsem si vytvořil něco vzdáleně připomínající (dost omezená funkcionalita) váš orm. Dnes mě napadlo, že už to někdo musel řešit ..takže jsem strávil dvě hodiny průzkumem webu až jsem konečně narazil na toto a strašně moc se mi to líbí. Už se těším až to použiji na ostro. Tak jsem si konečně vytvořil účet na foru abych vám napsal good work! :))

uestla
Backer | 799
+
0
-

Verze 2.0.0

  • GitHub
  • přepsáno pro Nette 2.1
  • zavrženy magické gettery/settery (IDE stejně neumí napovědět, ani navést na místo definice)
  • verze 1.x ve zvláštní větvi

Pokud narazíte na nějakou chybu, neváhejte ji nahlásit. Díky a ať vám nástroj slouží.

Editoval uestla (5. 1. 2014 4:25)

sKopheK
Člen | 207
+
0
-

Jak moc je to závislé na NDB? U něj jsem měl problém s https://forum.nette.org/…dotazy-cache který se podle mě vyskytuje i zde (kvůli lazy loadingu). Jde nějak vynutit načtení dat ihned po dotazu bez nějakého pseudo procházení apod.?

uestla
Backer | 799
+
0
-

Na NDB je to v podstatě postavené. Je možné, že k něčemu podobnému, co popisuješ, dochází. Nemáš nějaký minimální „skeleton“, ve kterém je tohle chování demonstrované?

sKopheK
Člen | 207
+
0
-

Bohužel – je to složitější projekt o více než dvaceti tabulkách, kde se provádějí výpočty s několika záznamy z databáze zaráz a pokud si data hned po vytažení z DB neuložím do pomocných objektů, dostávám někdy dost podivné výsledky.

uestla
Backer | 799
+
0
-

Je pravda, že na takhle složitější věci se takovéhle nástroje, které nejsou schopné zajistit integritu jednotlivých instancí (není tu IdentityMap ani podobné patterny), příliš nehodí. Komplikace podobného typu holt nelze řešit jinak než ručním ohlídáním v aplikaci…

rixi
Člen | 109
+
0
-

Diky za skvelu kniznicu, je napisana minimalisticky, cita sa aj pouziva sa skvele.

Akurat som natrafil na bug suvisiaci s NDBT, pokial sa pouziva tabulka bez autoinkrementu na id, tak to kniznica nespracuje (konkretne v https://github.com/…pository.php#L162).

Otazne je co s tym https://github.com/…/issues/1421.

fero.peterko
Člen | 3
+
0
-

Ahoj, můžu mít dotaz, proč je metoda __call v YetORM\Entity označena jako final? Bohužel je to pro mě poněkud omezující.

uestla
Backer | 799
+
0
-

@fero.peterko: To je fakt, nemuselo by to být final… Pošli pullík a začlením to.

uestla
Backer | 799
+
0
-

Verze 5

  • GitHub (diff oproti verzi 2)
  • přepsáno pro Nette 2.2 (upraveny závislosti pouze na potřebných částech Nette)
  • lepší pojmenování metod a proměnných (vizte prosím commity)
  • odstraněny všechny nadbytečné metody z Entity (zůstaly pouze magické metody, toRecord() a kešovaná reflexe)
  • změněn zápis anotace mapovaného sloupce z $<property> -> <column> na $<property>:<column>
  • odebrána magická detekce entitní třídy a tabulky na základě názvu třídy repozitáře – entitní třídu i tabulku je nutno specifikovat vždy
  • přidána zkratka Repository::transaction($callback) pro transakci
  • odstraněno final všude, kde to mělo smysl :-)
  • přístup k neinicializovaným hodnotám na Record vyhodí výjimku

Rád bych napsal nějaký jednoduchý ukázkový tutoriál (asi vyjdu z modelu v dokumentaci) a přidal to do doplňků…

Pokud narazíte na nějakou chybu, neváhejte ji nahlásit. Díky a ať vám nástroj slouží.

Zax
Člen | 370
+
0
-

@uestla: Jako bys mi četl myšlenky! zrovna asi před hodinou jsem opět polemizoval nad modelovou vrstvou, nad tím, zda se mám už konečně začít učit Doctrine i nad tím, zda YetORM bude pokračovat někam dál. Odstranění final by mohlo znamenat, že už nemusím tvořit vlastní ORM, ale můžu si rozšířit YetORM. Díky!

Mimochodem, máš v plánu tuto knihovnu dál vyvíjet? Například by se mi líbila lepší podpora vazeb, třeba toto kdyby šlo zapsat jednoduše pomocí anotací třeba v tomto stylu

/**
 * @property Author $author:author
 * @property Tag[] $tags:book_tag.tag
 */

Něco podobného jsem zkoušel u svého ORM a celkem to šlapalo, akorát jsem nevyřešil snadné přidávání třeba těch tagů a toto se mi upřímně taky moc nelíbí. Nakonec to navzdory všem doporučením řeším tak, že i spojovací tabulky mají entity a repozitáře. Ono je to ve finále asi i nejlepší řešení, vzhledem k tomu, že se klidně může po nějaké době objevit nějaký požadavek na doplňující informace k vazbám.

Nicméně, great job!

uestla
Backer | 799
+
0
-

@Zax: Osobně u svých entit moc anotační zápis propert nepoužívám – jednak je takový neohrabaný, IDE mi sice napoví, ale navenek se to tváří jako že zapisuju do public property třídy, ale hlavně když chci nějakou složitější logiku, tak jediná rozumná možnost je si ten getter/setter napsat.

Navíc zápis @property Author $author:author by nakonec potřeboval i syntaxi pro případ, kdyby byl autor navázán na tabulku author ještě jiným sloupcem (např. maintainer_id v ukázce v dokumentaci) – a to už si raději to $this->record->ref('author', 'maintainer_id'); napíšu…

V tomhle směru tedy anotace moc rozšiřovat neplánuji.

Můj úmysl byl okleštit entitní třídu na minimum, což se mi jakž takž povedlo (žádné nadbytečné metody, které by mohly způsobovat zbytečné kolize). Mám napsáno něco na způsob lazy bindingu hodnot vč. podpory validace, ale je to zatím v raném stádiu a kdo ví, jestli to někdy dospěje použitelného konce…

V tuhle chvíli si YetORM zachoval svoji naivitu a jednoduchost – mj. i v nedořešeném přidávání/odebírání entitních vazeb do/z kolekce (odkazované řešení se mi taky nelíbí, ale dokážu s tím žít). Takže i když knihovnu plánuji samozřejmě postupně zlepšovat, tak jsem zatím spokojen :-)

Zax
Člen | 370
+
0
-

Zápis přes anotace se dá parádně rozšiřovat, stačí vymyslet syntax, třeba

@property Author $author:author (through:maintainer_id)

V závorkách by mohly být věci typu limit, where, group, having u kolekcí, maxlength u normálních typů apod… Je fakt že anotace asi nejsou zrovna nejčistší řešení, ale zato jsou sakra rychle napsaný. A metody, které je obslouží, klidně mohou být private :-)

Ale samozřejmě tě nebudu přesvědčovat, tenkost je skvělá, YetORM je čitelný a vstupní bariéra je třeba oproti Doctrině prakticky nulová, přitom je to i hezky rozšiřitelné a nemám problém si to doprogramovat, spíš mě jenom zajímalo, jestli něco takového sám nechystáš ;-)

uestla
Backer | 799
+
0
-

Sypu si popel na hlavu. Oháním se tu cílením na code completion v IDE nástrojích, a přitom nesmyslně změním zápis anotace mapovaného sloupce (resp. alespoň NetBeans napovídají property jako celou, tj. včetně dvojtečky – v novém hloupém zápisu totiž nejsou mezery).

Rozhodl jsem se tedy zápis vrátit zpět na šipkový – byl jsem tedy nucen v rámci udržení nějaké sémantičnosti verzování zvýšit verzi na 6. Omlouvám se za případné komplikace ;-)

Gappa
Nette Blogger | 208
+
0
-

Zdravím,

chtěl bych se zeptat – jak by se řešily přes YetORM jazykové mutace? Např. pro situaci, kdy v tabulce jsou sloupce odlišené stylem title_cs/title_en apod.

Nebo jestli tohle celé je blbost a má se to řešit úplně jinak? :)

Předem díky za případné info

uestla
Backer | 799
+
+3
-

YetORM je low-level jako prasátko, logiku ohledně mutací aj. (prostě vyvoněný steak) si člověk musí vyrobit sám – ať už předáváním aktuální lokálie do entity, nebo řešením parametrizovaných getterů/setterů (getTitle($lang), apod.) :-)

Gappa
Nette Blogger | 208
+
0
-

Ok, díky za vysvětlení :)

webdata
Člen | 153
+
+1
-

Zdravím,

Jelikož hodně používám YetORM i když je svým způsobem primitivní ale dostačující i na velké aplikace, tak jsem narazil na problém jak nejrychleji vytvořit repozitáře a entity z databáze. Proto jsem si vytvořil velice primitivní script který tohle udělá. Pokud tedy máte 10 a více tabulek tak se vám tento script bude třeba hodit. Script stačí nahrát někde kde je možné ho spustit. Pak nastavit připojení k požadované databázi a spustit. Script pak vytvoří adresáře models a models/entity kam vygeneruje patřičné třídy. Nehodící se třídy pak jednoduše smáznete.

yetorm-build.php

<?php

/**
 * Vytvoření repozitářů a entit z tabulek databáze.
 *
 * @filesource	yetorm-build.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 */
/** Databázový ovladač */
define('DB_DRIVER', 'mysql');

/** Adresa SQL serveru */
define('DB_HOST', 'localhost');

/** Název databáze */
define('DB_NAME', 'nazevdatabaze');

/** Přihlašovací jméno */
define('DB_USER', 'root');

/** Přihlašovací heslo */
define('DB_PASSWORD', 'root');

/** Namespace tříd */
define('NS', 'App\\');

// Přípojení do databáze,
$db = new PDO(DB_DRIVER . ':host=' . DB_HOST . ';dbname=' . DB_NAME, DB_USER, DB_PASSWORD, array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES UTF8'));

// Načtení názvu tabulek,
$tables = array();

foreach ($db->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN) as $table) {

	$name = implode('', array_map(function($word) {
				return ucfirst($word);
			}, explode('_', $table)));

	$cols = array();

	foreach ($db->query("DESCRIBE `$table`") as $col) {
		$cols[$col['Field']] = '@property' . (strtolower($col['Extra']) == 'auto_increment' ? '-read' : NULL)
				. ' ' . getColType(strtolower($col['Type'])) . (strtolower($col['Null']) == 'yes' ? '|NULL' : NULL)
				. ' $' . $col['Field'] . "\n";
	}

	$tables[$name] = array(
		$table => $cols,
	);
}

// Generování tříd,
if (count($tables)) {
	foreach (array('\models', '\models\entity') as $path) {

		if (!file_exists(__DIR__ . $path)) {
			mkdir(__DIR__ . $path);
		}
	}

	foreach ($tables as $className => $table) {

		foreach ($table as $name => $cols) {

			// Repozitář,
			$properties = " * @table $name\n * @entity \\" . NS . 'Models\Entity\\' . $className;
			$buffer = "namespace " . NS . "Models\\Repository;\n\n/**\n$properties\n */\nfinal class $className extends \\" . NS . "Models\\Repository\\BaseRepository\n{\n\n}";
			file_put_contents(__DIR__ . '/models/' . $className . 'Repository.php', "<?php\n\n$buffer\n");

			// Entita,
			$properties = ' * ' . implode(' * ', $cols);
			$buffer = "namespace " . NS . "Models\\Entity;\n\n/**\n$properties */\nfinal class $className extends \\" . NS . "Models\\Entity\\BaseEntity\n{\n\n}";
			file_put_contents(__DIR__ . '/models/entity/' . $className . 'Entity.php', "<?php\n\n$buffer\n");
		}
	}
}

/**
 * Detekce typu sloupce
 * @param string $type typ sloupce
 * @param array $outputArray
 * @return string
 */
function getColType($type, $outputArray = NULL)
{
	preg_match("/\w+/", $type, $outputArray);

	$types = array(
		'string' => array('varchar', 'text'),
		'\Nette\Utils\DateTime' => array('datetime'),
	);

	foreach ($types as $_type => $array) {

		if (in_array(strtolower(trim($outputArray[0])), $array)) {
			$outputArray[0] = $_type;
			break;
		}
	}

	return $outputArray[0];
}

Ještě jednou upozorňuji, že se jedná o velice primitivní script. Nakládat sním můžete dle uvážení.

Editoval webdata (10. 9. 2014 23:56)

Jan Suchánek
Člen | 404
+
0
-

@webdata Moc pěkné!

webdata
Člen | 153
+
0
-

Takže asi jsem objevil bug a to v poslední verzi. Jsem tak kapánek zvyklej dělat komenty. Takže to co mě šlo v předchozí verzi už teď nejde. Takže tohle v předposlední verzi šlo:

<?php

namespace App\Model\Repository;

/**
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 *
 * @table centre
 * @entity \App\Model\Entity\Centre
 */
final class Centre extends \App\Model\Repository\BaseRepository
{
.
.
.
}

Teď to hlásí (výpis s tracy řádek 175)

165:        {
166:            return $this->database->table($table === NULL ? $this->getTableName() : $table);
167:        }
168:
169:
170:        /** @return string */
171:        final protected function getTableName()
172:        {
173:            if ($this->table === NULL) {
174:                if (($annotation = static::getReflection()->getAnnotation('table')) === NULL) {
175:                    throw new Exception\InvalidStateException("Table name not set.");
176:                }
177:
178:                $this->table = $annotation;
179:            }

Když to upravím takto:

/**
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 */

/**
 * @table centre
 * @entity \App\Model\Entity\Centre
 */
final class Centre extends \App\Model\Repository\BaseRepository
{
.
.
.
}

Tak to valí. Ale mě se to nelíbí. Některé komentáře jsou klíčové, protože na projektu dělá více lidí.

PS: to same plati i pro entity.

Editoval webdata (9. 9. 2014 23:15)

uestla
Backer | 799
+
0
-

Předchozí verze byla jaká?

Myslím, že to bude ta, ve které ještě nebylo povinné specifikovat jméno tabulky a třídu entity (protože se detekovala ze jména třídy repozitáře).

Tohle bude ale nejspíš souviset s parsováním anotací, kterémužto se nelíbí prázdný řádek mezi jedn. anotacemi. Zkus ho prosím odstranit, jestli to pomůže (stejný problém bude nejspíš i u entitních tříd, kde se ti kvůli tomu nebudou definovat anotační property).

webdata
Člen | 153
+
0
-

Bohužel ani toto nepomohlo:

/**
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 * @table centre
 * @entity \App\Model\Entity\Centre
 */

100% to šlo v 6.0.3 v 7.0.0 si nejsem jistej a teď používám 7.0.1

Tam to vypadá, že to proste musí bejt hned na začátku po /** protože tohle pak funguje

/**
 * @table centre
 * @entity \App\Model\Entity\Centre
 *
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 */
uestla
Backer | 799
+
0
-

Bohužel se mi nedaří chybu zreplikovat. Při použití uvedených anotací vše funguje.

U entit jsi psal, že to také chybuje, jenže asi jiným způsobem. Jakým?

webdata
Člen | 153
+
0
-

Takže ještě jedno zjištění.

Používám Nette 2.2.3. A poslední verzi YetORM 7.0.1.

Tohle prostě reflexe v nette (AnnotationsParser) nezvládne:

/**
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 *
 * @table centre
 * @entity \App\Model\Entity\Centre
 */

dump výsledku static::getReflection()->getAnnotations()

array (1)
description => array (1)
0 => "Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 *
 * @table centre
 ... " (185)

Ale pokud to upravím takto, tak to jede v pohodě.

/**
 * @jednoco Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 *
 * @table centre
 * @entity \App\Model\Entity\Centre
 */

dump výsledku static::getReflection()->getAnnotations()

array (6)
jednoco => array (1)
0 => "Repozitář středisek." (23)
filesource => array (1)
0 => "CentreRepository.php" (20)
author => array (1)
0 => "© Web Data Studio, www.web-data.cz" (35)
version => array (1)
0 => "1.0.0" (5)
table => array (1)
0 => "centre" (6)
entity => array (1)
0 => "\App\Model\Entity\Centre" (24)

Nette reflexe (AnnotationsParser) z nějakého důvodu to nezvládne parsovat.

zatím řeším takto (kvuli APIgen – komentář do dokumentace)

/**
 * @table centre
 * @entity \App\Model\Entity\Centre
 *
 * Repozitář středisek.
 *
 * @filesource	CentreRepository.php
 * @author		© Web Data Studio, www.web-data.cz
 * @version		1.0.0
 */

Editoval webdata (10. 9. 2014 14:16)

Šaman
Člen | 2661
+
0
-

Založ na to novou issue, nebo aspoň vlákno, ať si toho všimne i @DavidGrudl. Myslím, že tyhle diskuze zaměřené na konkrétní doplněk nečte.

uestla
Backer | 799
+
0
-

Nejdřív bych rád aspoň zreplikoval chybu, než zakládal issue, která může být neopodstatněná (a navíc chybu bych spíš předpokládal u YetORM než u Nette). Jelikož ale i s phpDocem uvedeným nahoře mi testy procházejí, tak mě napadá, že by chyba mohla být jen v kódování, které by třeba AnnotationParser nezvládl…

Máš ty skripty v utf8, @webdata ?

Šaman
Člen | 2661
+
0
-

No, mě to taky přišlo divný, že by to ten uvedený pdpDoc nefungoval, jsem přesvědčený, že dřív rozhodně šlo kombinovat popis a anotace. I u YetORMu. Až dotáhnu práci, tak ten parser testnu.

webdata
Člen | 153
+
0
-

Takže chyba na mé straně. A docela záludná a způsobená asi nějakým bugem v MATRIXu. To UTF-8 mne nakoplo. Dělám v NetBeans a ještě nikdy jsem neměl problém že by soubor nebyl v UTF-8. Hned jsem to teda zkontroloval. Bohu dík soubor měl kódování UTF-8. Tak v čem je problém? Ješte jsem to otevřel ve Far Manager Viewer jako HEX a potom jsem to uviděl.......

Konec řádku byl jako 0×0D místo 0×0A …

Takže moje chyba.

Editoval webdata (10. 9. 2014 23:58)

uestla
Backer | 799
+
0
-

Jojo, MATRIX je svině… Každopádně díky za report i objasnění.

webdata
Člen | 153
+
+1
-

Zde je odkaz na script co z tabulek v databázi vytvoří repository a entity třídy.

yetorm-builder.php

Pavel Kravčík
Člen | 1195
+
0
-

YetORM se mi opravdu líbí. Funguje to bezvadně. Jen mám možná jeden hloupý dotaz.

/**
 * @property string $jmeno
 * @property string $prijmeni
*/
class Uzivatel extends BaseEntity
{
	private $jmeno;
	private $prijmeni;
}

To je nějaké zkrácené klasické nastavení entity. A poté chci používat hloupé [gs]ettery. Hlavně kvůli nápovědě v IDE a možnosti validovat a upravovat data.

	public function getJmeno()
	{
		return $this->jmeno; //NULL
	}
	public function getJmeno()
	{
		return $this->record->jmeno; //Kundasemkundatam
	}

Entita se plní do record. Takže pokud zakládám novou entitu a vytvořím si private proměnné v třídě a podle nich si chci vygenerovat [gs]ettery, tak narazím. Musím všude nahradit return $this za return $this->record nebo $this->jmeno = $jmeno za $this->record->jmeno = $jmeno.

S ORM zkušenosti nemám, ale proč se neplní přímo třída? Když pak chci používat ten objekt, abych viděl jaké přesně má funkce apod. Takže udělám něco špatně, když si rozšířím konstruktor a přeleji si data do objektu → budu je používat a před uložením je vrátím do record? Nebo něco ohýbám a znásilňuji způsobem nepěkným hodným maximálně našeho presidenta?

Díky za rady.

uestla
Backer | 799
+
0
-

Record uvnitř entity je obálka nad instancí Nette\Database\Table\ActiveRow, a je to z důvodu pozdější manipulace se změněnými hodnotami při persistování. Duplikovat si hodnoty do privátních proměnných můžeš v custom [gs]etterech, je to ale IMHO zbytečnost, když už jsou v $recordu (pokud nevyžaduješ nějakou jinou logiku).

Pavel Kravčík
Člen | 1195
+
0
-

Díky. Myslel jsem si to. Je důležité, aby s entitou umělo pracovat IDE (ideálně přes settery) a v těch setterech byla nějaká další logika (úpravy hodnot, kontrola rozsahu, výjimky v případě prázdných proměnných). Prostě aby ta entita byla trochu chytřejší.

Nejrozumnější mi v téhle chvíli připadají custom [gs]ettry, které budou pracovat s $this->record->id.

Edit: Ještě přidám, že pro protected/private hodnoty jsem to chtěl přepsat z důvodu automatického generování [gs]ettrů v Netbeans. Ta entita bude mít třeba 100 různých proměnných. Práce s exporty excelů je děs. :)

Editoval kzk_cz (31. 12. 2014 8:45)

Pavel Kravčík
Člen | 1195
+
0
-

Ještě bych měl jeden dotaz. Rozjíždím nový projekt. Takže chci implementovat YetORM a mám nejnovější sandobox Nette 2.2.7 tuším (na stránkách je 2.2.6, ale composer myslím tahal 7).

Presenter

namespace App;

class TestPresenter extends BasePresenter
{
    private $u;

    public function inject( testRepository $u)
    {
        $this->u = $u;
    }

    public function renderDefault()
    {
        $users = $this->u;

        /* @var $user UzivatelEntity */
        foreach ($users->findAll() as $user)
        {
            echo $user->id . '<br/>';
        }
        die();
    }
}

Repositář

namespace App;

/**
 * @table  uzivatel
 * @entity \App\UzivatelEntity
 */
class testRepository extends \YetORM\Repository
{}

Entita

namespace App;

/**
 * @property-read int $id
 * @property string $nick
 */
class UzivatelEntity extends \YetORM\Entity
{
    function getTest()
    {
    }
}

Když to mám takhle tak skript skončí chybou Class name must not be empty., předpokládám, že to nějak souvisí s YetORM Repository řádek 194. Který čte anotace a tam vyhazuje tu výjimku. Pokud smažu funkci getTest() nebo ji přejmenuji na gTest(), tak vše funguje dobře a presenter vypíše ID všech záznamů. Takže pokud v entitě nepoužívám funkce s prefixem get je to v pořádku. Přišel jsem na to náhodou, když jsem přidával Grido, které nevolá getFunkce().
Protože v případě, že definuji funkci getTitle(), tak s ní můžu pracovat pokud jí nastavím aby vracela příslušný záznam z $this->record. Ale poté už nefunguje klasické $user->title.

Dělám něco špatně nebo proč to tak funguje? Rád bych přistupoval k proměnným „obyčejně“ $user->title a k těm speciální třeba $user->getArticles(). Vlastně jako je to v příkladu na Gitu. Ale jak definuji nějakou funkci s tím prefixem get → každé volání $user->title skončí chybou.

Díky za nakopnutí, ať už bolestivé nebo správným směrem.

uestla
Backer | 799
+
0
-

V kódu, cos sem poslal, problém nevidím, každopádně podle chyby to vypadá, že AnnotationParser dostává prázdnou třídu – což by ale nemělo nastat, protože by mělo zařvat už Repository, že není definovaná entitní třída (což evidentně je). Zkus prosím entitu nastavit pomocí členské proměnné:

/** @table  uzivatel */
class testRepository extends \YetORM\Repository
{
	/** @var string */
	protected $entity = '\App\UzivatelEntity';
}
Pavel Kravčík
Člen | 1195
+
0
-

To jsem zkoušel. Výsledek byl stejný. Ano na to jsem koukal. Zvláštní je, že výjimka vyskočí až z Nette. Nesouvisí to tedy nějak s DI?

Zapomněl jsem to napsat, že jsem zkoušel zápisy i přes protected proměnné a také bez namespace. Bez namespace to funguje stejně. Resp.

	protected $entity = 'UzivatelEntity';

Ale jak mi to hlásilo prázdnou třídu, tak jsem tam radši dal absolutní cestu. Je to strašně zvláštní chyba právě. Objevil jsem ji jinde, ale nejdříve jsem to ještě zkusil na sandboxu, než jsem to sem dal. To jsou právě jen ty test skripty.

uestla
Backer | 799
+
0
-

Zkus, co vypíše

dump(App\testRepository::getReflection()->getAnnotation('table'));
Pavel Kravčík
Člen | 1195
+
0
-

dump(\App\testRepository::getReflection()->getAnnotation(‚table‘));

"uzivatel" (8)

$users = $this->u;
dump($users::getReflection()->getAnnotation(‚table‘));

"uzivatel" (8)

Takže předpokládám, že přes DI to probublá ok. To samé jde i pro getAnnotation(‚entity‘).

uestla
Backer | 799
+
0
-

Teda myslel jsem „entity“, ne „table“, samozřejmě. Ale jestli to vypisuje správně tu třídu, jsi si jistý, že tu chybu to vyhazuje v tom místě, které jsi poslal? Nemáš ještě nějaký jiný repozitář bez defiované entitní třídy?

Pavel Kravčík
Člen | 1195
+
0
-

No, já jsem kvůli tomu stáhnul komplet nový sandbox. Protože na rozdělaném projektu jsem tam měl moc věcí okolo (baseEntity, baseRepository apod.). A zkusil to co „nejhloupěji“ na něm. Asi se podívám i na Git jaké jsou verze YetORM a Nette. A nebo po práci si to vyzkouším ještě doma. Ale je to opravdu opravdu zvláštní chyba/chování. :)

Případně uploadnu čistý sandbox s těmi 3 soubory někam.

Pavel Kravčík
Člen | 1195
+
0
-

Tak jsem vyzkoušel i na jiném PHP a dvě verze a stále stejný problém.

Přikládám přílohu 1MB (sandbox Nette + YetORM) a to jak jsem to nastavil (mělo by být „skoro“ duplicitní s tím co jsem postnul). Nejzajímavější ale opravdu je, že když z té entity smažeš tu funkci tak to frčí. :D Minimálně je tam vidět klikací laděnka. Stačí nahrát databázi (je tam SQL dump) a pustit:

http://127.0.0.1/yetORM/sandbox/www/test/

http://nahrajsoubor.cz/…66574777bd15

Editoval kzk_cz (6. 1. 2015 13:26)

uestla
Backer | 799
+
+2
-

Tak už vím. Chyba nebyla v Repository, ale v reflexi, kde se skenují get*() metody v entitě a hledá se návratový typ podle @return anotace, která ti v entitě chyběla – což by ale nemělo končit chybou, uznávám.

Mělo by to být opraveno v nové verzi.

Pavel Kravčík
Člen | 1195
+
0
-

@uestla: Luxus! Dobrá práce.

A kam by to return patřilo a co by mělo vracet? Před definicí class nebo pro ty get metody? Zkoušel jsem to podle příkladů a nikdy jsem to neviděl. Ale to je jen mimo soutěž.

Funguje to super a už se těším, až se do toho dnes vrhnu. :)

A btw nějaký paypal/follow nebo adresa kam poslat mladičkou striptérku za odměnu?

uestla
Backer | 799
+
0
-

Klasicky jako anotaci před metodou

/** @return int */
function getID()
{
	return $this->record->id;
}

Striptérku můžeš poslat přes PayPal, třeba přes link tady vpravo (pokud nemáš AdBlock).

Pavel Kravčík
Člen | 1195
+
0
-

Jasně rozumím. Super. Díky.

Nevím, jestli se tam vejde celá – budu jí teda posílat po částech. :) (AdBlock mi to skryl)

Pavel Kravčík
Člen | 1195
+
0
-

Ještě mi napadá jeden dotaz: (sorry že jsem otravný)

Vycházejme z tohohle kódu, co jsem již odeslal. Pokud tohle změním:

/**
 * @property int $id
 * @property string $title
 */
class TestEntity extends \YetORM\Entity
{
    function getTest()
    {
        //smazat a funguje
    }
}

Přepíši funkci getTest() na getId():

	function getId()
{
		return 'striptérka-' . $this->id;
}

Tak procházení foreach v presenteru skončí chybou na $entita->id. A to jen v případě, že se funkce get jmenuje stejně jako volaná proměnná.

Cannot read an undeclared property App\TestEntity::$id.

Tedy není možnost přistupovat k entitě přes $entita->id a zároveň mít v entitě funkci getId(). Může to být matoucí v mém projektu a tyhle dva výsledky mohou vracet dvě různé hodnoty na stejný objekt (pokud bude v getId() nějaká funkčnost). Tohle řešení by fungovalo:

	function _getId()
{
	return 'striptérka-' . $this->id;
}

Je to chyba nebo feature?

Editoval kzk_cz (7. 1. 2015 10:55)

uestla
Backer | 799
+
0
-

Property fungují tak, že implicitně překládají

$entity-><property>;

na

$entity->record-><property>;

A magický přístup, který je v Nette\Object, čili volání getterů a setterů, jsem z entity odstranil, protože mám rád, když mi IDE napovídá.

Pavel Kravčík
Člen | 1195
+
0
-

Jasně tomu rozumím.

Já bych jen rád měl v entitě funkci, kterou si napíši sám i přestože už tam mám @property int $id.

function getId(){}

To možné tedy není?

Pavel Kravčík
Člen | 1195
+
0
-

A když už na to myslím. Nebylo by možné zpřístupnit property nullable? Nedá se k němu přistupovat jelikož je private v YetORM\AnnotationProperty.

	$ref = static::getReflection();
	$ref->getEntityProperties();
uestla
Backer | 799
+
0
-

Možné to je, ale každá věc pak logicky bude vracet něco jiného (property vs. metoda).

Nullable done.