Nextras\ORM – ORM nad Nextras\Dbal
- petr.pavel
- Člen | 535
@w3r0 Nebude to tím, že máš v entitě „int nebo null“, přičemž null to být nesmí, když je to primární klíč? V db to je správně NOT NULL.
@property int|null $id {primary}
Asi se tam snažíš vložit null.
P.S. Založ si, prosím, samostatné vlákno, tohle je kilometr dlouhé, míchá se tu kde co.
- David Matějka
- Moderator | 6445
@raddy668 ahoj,
- v doc je chyba. od verze 3.0 je potreba ten query builder prohnat skrz toCollection, stejne jako to delas s tim resultem
- s tema properties je to divne, nejsou treba jejich hodnoty null?
- manwe
- Člen | 44
@DavidMatějka
Jo, uz jsem to poresil :) Ma tam nekdo pristup to opravit? Ja totiz dost dlouho
stravil nad tim ze jsem faral co mam blbe proti tomu examplu :D
Promin, nevsiml jsem si zes odpovedel driv nez jsem to smazal.
Bylo to zpusobeno tim, ze jsem mel v anotacich property v snake_case
i v camelCase a Nextras je v defaultnim modu automaticky predelava
z camelCase do snake_case a timpadem ty property, ktere byly ve snake_case tak
se nenaplnovaly.
Takze muj brainfart, predelaval jsem to z YetORM do Nextrasu.
Kazdopadne diky za odpoved :)
- David Matějka
- Moderator | 6445
@raddy668 dokumentace je na githubu, takze kdokoliv muze poslat PR s opravou :) (krome stranky s mapperem je chyba i v repository kapitole) poslal bys PR nebo to mam opravit ja? :)
- d3tr1tus
- Člen | 52
Ahoj začínám s ORM a jeho implementací píše mi to zatím jen
Class App\Model\Orm does not exist
můj config
extensions:
nextras.orm: Nextras\Orm\Bridges\NetteDI\OrmExtension
nextras.orm:
model: App\Model\Orm
strom modelu
-model
-orm
-Order
-OrderRepository.php
-atd.
-Orm.php
-SomeService.php
co ještě mám přidat? Díky moc za rady :)
- David Matějka
- Moderator | 6445
@d3tr1tus tohle je autoloading nextras/dbalu, ne? jde o tvuj autoloading tveho projektu. predpokladam, ze pouzivas robot loader. mas tu slozku model ve slozce app?
- Ondřej Kubíček
- Člen | 494
ty si ale musíš hodit do autoloadu třídy co máš ve složce app, takže musíš mít v bootstrapu něco takového
máš?
$configurator->createRobotLoader()
->addDirectory(__DIR__)
->register();
nebo tu tvoji třídu máš uplně někde mimo?
- David Matějka
- Moderator | 6445
ten je predpokladam ve vendoru, ten te vubec nemusi zajimat. zkontrol ji, ze
mas ten soubor Orm.php spravne – ze ma opravdu spravnou koncovku
.php
, ze je nekde ve slozce app a ze ma spravnou oteviraci
znacku <?php
- David Matějka
- Moderator | 6445
a s autoloadingem jinych trid problem nemas? zkus treba ten soubor smazat a znovu vytvorit, treba i pod jinym nazvem
- Zax
- Člen | 370
Ahoj,
používám Nextras ORM spolu s pluginem pro PhpStorm.
Všiml jsem si, že se mi v repozitářích nabízí tlačítka „Navigate
to mapper method“, které bohužel nic nedělají. Ono se to řídí nějakou
konvencí? Dá se to nějak nastavit, aby to fungovalo i u mě? Repozitáře
mám pod NS Modul\Submodul\Repository
a mappery mám pod
Modul\Submodul\Mapper
.
Dále se chci zeptat – plánujete přidat také tlačítka „Navigate to getter/setter method“ do entit?
Díky za případnou odpověď a hlavně za toto fantastické ORM!
- hrach
- Člen | 1838
@Zax v plugingu toto dela tato trida: https://github.com/…rProvider.kt Ktera aktualne stripuje Repository suffix a pridava Mapper suffix. Tvuj zpusob namingu tedy uplne fungovat nebude, asi by bylo dobre ten plugin upravit, muzes otevrit issue/pull request :)
- ony2
- Člen | 9
hrach napsal(a):
@ony2 jedine pres custom collection (filtering) function.
Dakujem, spravil som to cez filter function, len pre istotu som chcel vediet ci nie je aj lahsia cesta.
- mskocik
- Člen | 62
Ahoj, zasekol som sa na nasledovnej veci:
Tabulka User
Field | Type | Null | Key | Default | Extra |
id | int(11) | NO | PRI | NULL | auto_increment |
username | varchar(255) | NO | UNI |
Tabulka Contact
Field | Type | Null | Key | Default | Extra |
id | int(10) unsigned | NO | PRI | NULL | auto_increment |
user_id | int(10) unsigned | NO | PRI | NULL | |
type | enum(‚billing‘,‚personal‘) | NO | PRI | NULL | |
name | varchar(100) | NO | NULL |
Neviem, ako spravne definovat vztah {1:1}
tak, aby bolo
mozne pouzit
$user->contact
Je to vobec mozne, ked User
nema referenciu na
Contact
?
- David Matějka
- Moderator | 6445
@mskocik ahoj, mrkni do doc, „Book“ je v tvem pripade ten „Contact“, jelikoz ten drzi referenci..
- mskocik
- Člen | 62
@DavidMatějka vdaka za reakciu. Mam to takto:
/**
* @property int $id {primary}
* @property Contact $contact {1:1 Contact::$user} // v DB nie je stlpec contact_id
*/
final class User extends Entity {}
/**
* @property int $id {primary}
* @property User $user {1:1 User::$id, isMain=true} // v DB stlpec user_id
*/
final class Contact extends Entity
Predpokladam, ze je to takto spravne, pretoze vazba Contact na Usera, je cez
user_id
. Dava mi to takto zmysel ale vysledkom je:
Nextras\Orm\InvalidStateException
Model\User\User::$contact relationship with Model\User\Contact::$user is not symetric.
Co mi tu unika? Tabulka User nema definovany
contact_id
(z historickych dovodov) – predpokladam, ze kebyze
je to tam definovane, tak by to fungovalo bez problemov. Je mozne nieco taketo
vobec? V Doctrine1 s tym neboli problemy nikdy :)
- David Matějka
- Moderator | 6445
@mskocik
u toho mapovani Contact::$user musis mit
{1:1 User::$contact, isMain=true}
, uvadi se tam inversed strana
te relace
- mskocik
- Člen | 62
@DavidMatějka v takejto forme mi to nefungovalo – vynimka:
Nextras\Dbal\QueryException
Unknown column 'Contact.user' in 'where clause'
SELECT `Contact`.* FROM `Contact` AS `Contact` WHERE `Contact`.`user` IN (3393) // existuje `Contact`.`user_id`
Musel som este zmenil
* @property Contact $contact {1:1 Contact::$user}
na
* @property Contact $contact {1:1 Contact::$userId}
zacalo to
fungovat. Dakujem moc!
/
- Václav Kraus
- Člen | 77
Ahoj, je možné se jednoduše připojit k více databázím?
V configu mám
nextras.dbal:
db1:
driver: mysqli
host: db
username: root
password: root
database: db1
db2:
driver: mysqli
host: db
username: root
password: root
database: db2
Netuším ale, jak jednotlivá připojení předat ORM. Předem díky za tip :)
- mskocik
- Člen | 62
@VáclavKraus Ahoj, k databazi sa pripaja mapper, takze v configu si mozes pre dany mapper definovat, ktore Dbal\Connection pouzijes:
services:
- UserMapper # toto pouzije dane connection, ktore mas definovane autowired
- ContactMapper(@nextras.dbal.db2.connection)
nextras.dbal:
db1:
driver: mysqli
host: db
username: root
password: root
database: db1
db2:
driver: mysqli
host: db
username: root
password: root
database: db2
autowired: false
- Václav Kraus
- Člen | 77
Děkuju vám za rady. Nicméně jsem se zasekl na tom, že je stejně předána connection, která má nastavený autowire.
Mám v konfigu:
extensions:
nextras.orm: Nextras\Orm\Bridges\NetteDI\OrmExtension
nextras.dbal.gui: Nextras\Dbal\Bridges\NetteDI\DbalExtension
nextras.dbal.ldap: Nextras\Dbal\Bridges\NetteDI\DbalExtension
nextras.dbal.gui:
driver: mysqli
host: db
username: root
password: root
database: gui
autowired: false
nextras.dbal.ldap:
driver: mysqli
host: db
username: root
password: root
database: ldap
services:
- App\Orm\UserMapper(@nextras.dbal.gui.connection)
V cache se správně vytvoří:
public function createService__42_App_Orm_UserMapper(): App\Orm\UserMapper
{
$service = new App\Orm\UserMapper(
$this->getService('nextras.dbal.gui.connection'),
$this->getService('nextras.orm.mapperCoordinator'),
$this->getService('41_Nette_Caching_Cache')
);
return $service;
}
Nicméně UserMapper stále používá jinou connection. Nevíte, v čem by mohl být zádrhel? :)
- hrach
- Člen | 1838
@VáclavKraus pokud pouzivas phpdoc finder (default), tak ten, pokud
chces predefinovat sluzby, vyzaduje, aby mely presne jmeno v di. https://github.com/…ryFinder.php#L89
tzn. to jmeno cca nextras.orm.mappers.userRepository
(asi, podivej
se, co to presne vygenerovalo)
- mskocik
- Člen | 62
@pitr82 Ja som to spravil tak, že som si napísal vlastný bridge pre Dbal nejako takto:
<?php declare(strict_types=1);
namespace Base\Bridges\DI;
use Tracy\Debugger;
use Nextras\Dbal\Connection;
use Nette\DI\CompilerExtension;
use Nextras\Dbal\Bridges\NetteTracy\ConnectionPanel;
use Nextras\Dbal\Bridges\NetteTracy\BluescreenQueryPanel;
/**
* DBAL Extension enabling multiple connections and specific connection factory class
*/
class DbalExtension extends CompilerExtension
{
public function loadConfiguration()
{
$configs = $this->getConfig();
foreach ($configs as $name => $config) {
if (is_scalar($config)) continue;
$this->setupConnection($config, $name);
}
}
protected function setupConnection(array $config, string $name)
{
$builder = $this->getContainerBuilder();
$className = $config['factory'] ?? Connection::class;
unset($config['factory']);
$definition = $builder->addDefinition($this->prefix("$name.connection"))
->setClass($className)
->setArguments([
'config' => $config,
])
->setAutowired(isset($config['autowired']) ? $config['autowired'] : true);
if (isset($config['debugger'])) {
$debugger = $config['debugger'];
} else {
$debugger = class_exists('Tracy\Debugger', false) && Debugger::$productionMode === Debugger::DEVELOPMENT;
}
if ($debugger) {
$definition->addSetup('@Tracy\BlueScreen::addPanel', [BluescreenQueryPanel::class . '::renderBluescreenPanel']);
$definition->addSetup(ConnectionPanel::class . '::install', ['@self', $config['panelQueryExplain'] ?? true]);
}
}
}
A config.neon
extensions:
dbal: Base\Bridges\DI\DbalExtension
dbal:
default:
driver: mysqli
host: 127.0.0.1
database: db
username:
password:
logger:
# create: Base\Ext\Dbal\Connection # possible to define own connection factory
driver: mysqli
host: 127.0.0.1
database: dt_log
username: root
password:
- romiix.org
- Člen | 343
Zdravím, riešilo sa to tu už 100×, ale aj tam mi nič nepomáha…
V php nastavím:
$d = new \DateTime('2018-09-21 13:37:09');
// DateTime
// date => "2018-09-21 13:37:09.000000"
// timezone_type => 3
// timezone => "Europe/Prague"
$e = new E;
$e->date = $d;
Do DB sa zapíše do stĺpca date
(datetime) –
2018–09–21 13:37:09. Zatiaľ všeko ok.
Dám dump $e->date
a dostanem:
DateTimeImmutable
date => "2018-09-21 15:37:09.000000"
timezone_type => 1
timezone => "+02:00"
Config:
dbal:
connectionTz: auto
driver: mysqli
host: 127.0.0.1
database: db
username:
password:
Ako prosím zabezpečiť, aby som dostal aj naspäť zhodný čas ako som zapísal do DB?
Vďaka
- hrach
- Člen | 1838
@romiix.org
Jaky pouzivas v db datovy typ? Predpokladam, ze pouzivas
datetime
– ten neumi rozeznavat connection timezone, tzn. je mu
jedno, v jake jsi to ulozi a v jake to ctes. Takze uz je take spatne
zapsano 13:37, to uz je jiny udaj od toho posledniho. Koukam se a je to
dobre. Ten simple typ se chape v dbalu tak, ze ignoruje zonu, protoze
i databaze neumi pracovat se zonou. Tzn. asi je tam chyba fakt v tom cteni,
ale jsou na to testy a tam je to ok, tak bych to potreboval nejak vic
oddebugovat.
Pripadne prosim zaloz issue na githubu dbal. On tam je asi mozna opravdu bug.
Editoval hrach (24. 9. 2018 20:54)
- TonnyVlcek
- Člen | 31
@romiix.org @hrach
Teď jsem narazil na něco podobného, ale vypadá to spíš na chybu (změnu
chování) při zapisování do db. Nemůžu to říct se 100% jistotou, ale
zdá se, že problém (změna chování) se projevil po updatu na
nextras/orm 3.0.
V databázi jsem měl sloupec start_time
datového typu
DATETIME. V aplikaci mi uživatel vybral datum a čas a k tomu zvolil časovou
zónu. Při zpracování formuláře jsem si to přebral a vytvořil z toho
objekt DateTime se se správnou časovou zónou. Např.:
date => "2018-09-27 09:00:00.000000" (26)
timezone_type => 3
timezone => "Europe/Madrid" (13)
Do databáze se uložilo 2018-09-27 09:00:00.000000
místo
2018-09-27 07:00:00.000000
. (Čas serveru, php, mysql i connection
je nastavený na UTC).
Řešením bylo změnit datový typ sloupečku v db z DATETIME
na TIMESTAMP
a tím donutit nextras používat modifikátor
%dt
místo %dts
.
Nemyslím si, že tohle je nutně špatné chování, přijde mi, že to odpovídá i dokumentaci (https://nextras.org/…3.0/datetime#…). Zaskočilo mě ale to, že to vypadá, že se chování změnilo mezi verzemi.
Dá se Nextras Orm nějakým způsobem donutit aby používal
%dt
i pro slupce typu DATETIME?
- hrach
- Člen | 1838
@TonnyVlcek ahoj, v 3.0 opravdu doslo ke zmene chovani. Je to zminene tady a odkazuje se to sem, kde se pise:
Notable BC breaks:
Date-time changes: %dts does not do any timezone modifications (removed simpleStorageTz), read more in documentation and see the usage matrix.
Takze jednoduse receno a vysvetleno, sloupce, ktere nepodporuji zony –
DATETIME
– nyni DBAL taky chape tak, ze proste zadny zony nemaj.
Datetimy do nich zapisuje nehlede jejich zon. A cte je nehlede jejich zony. To
plati zejmena pro MySQL.
Coz je ten @romiix.org priklad – proste se vezme co je v DateTimu a neresi se, v jaky zone to je a zapise to. Pri cteni se to stejne tak precte z db a skonci to s php default timezonou.
V tvem pripade ti drivejsi chovani nahodou vic vyvhovalo, ale jak sam pises,
pro tvuj usecase je spravne pouzit datovy typ timestamp
. Mas nejaky
duvod chtit pouzit DATETIME
?
- Barbarossa
- Člen | 74
Ahoj,
nemůžu se prodrat přes jednu dbal/RelationshipMapperOneHasMany
exception:
Column 'id' in field list is ambiguous
Mám entitu ve které mám metodu:
public function getMembers()
{
return $this->userMembers->get()
->findBy([
'status' => 1,
'this->users->deleted' => FALSE,
])
->orderBy('priority', ICollection::DESC)
->limitBy(7);
}
tuto metodu volám v různých místech aplikace. Funguje všude až na jednu ajax komponentu (to, že je ajax na to ale asi vliv nemá), ve které se díky nějaké kombinaci (práci se stejnou tabulkou) vytvoří sql dotaz, který vyhazuje výše zmíněnou chybu. Kritické sql pak vypadá takto:
(
SELECT `id`, `teams_id` FROM `teams_access` AS `teams_access`
LEFT JOIN `users` AS `users` ON (`teams_access`.`users_id` = `users`.`id`)
WHERE ((teams_access.deleted = 0) AND ((`teams_access`.`status` = 1) AND (`users`.`deleted` = 0)))
AND (`teams_id` = 14)
ORDER BY `teams_access`.`teams_role_id` DESC LIMIT 7
)UNION ALL (SELECT `id`, `teams_id` FROM `teams_access` AS `teams_access` LEFT JOIN `users` AS `users` ON (`teams_access`.`users_id` = `users`.`id`) WHERE ((teams_access.deleted = 0) AND ((`teams_access`.`status` = 1) AND (`users`.`deleted` = 0))) AND (`teams_id` = 8359) ORDER BY `teams_access`.`teams_role_id` DESC LIMIT 7) UNION ALL (SELECT `id`, `teams_id` FROM `teams_access` AS `teams_access` LEFT JOIN `users` AS `users` ON (`teams_access`.`users_id` = `users`.`id`) WHERE ((teams_access.deleted = 0) AND ((`teams_access`.`status` = 1) AND (`users`.`deleted` = 0))) AND (`teams_id` = 8360) ORDER BY `teams_access`.`teams_role_id` DESC LIMIT 7) UNION ALL (SELECT `id`, `teams_id` FROM `teams_access` AS `teams_access` LEFT JOIN `users` AS `users` ON (`teams_access`.`users_id` = `users`.`id`) WHERE ((teams_access.deleted = 0) AND ((`teams_access`.`status` = 1) AND (`users`.`deleted` = 0))) AND (`teams_id` = 8361) ORDER BY `teams_access`.`teams_role_id` DESC LIMIT 7)
debug info:
RelationshipMapperOneHasMany->fetchByTwoPassStrategy()
RelationshipMapperOneHasMany->processMultiResult()
…vypadá to, že do metody fetchByTwoPassStrategy to chodí jen v té jedné ajax komponentě, protože ostatní vygenerují vždy něco jako:
SELECT DISTINCT `teams_access`.* FROM `teams_access` AS `teams_access`
LEFT JOIN `users` AS `users` ON (`teams_access`.`users_id` = `users`.`id`)
WHERE ((teams_access.deleted = 0) AND ((`teams_access`.`status` = 1) AND (`users`.`deleted` = 0))) AND (`teams_access`.`teams_id` IN (14))
ORDER BY `teams_access`.`teams_role_id` DESC LIMIT 7
+ pokud z findBy odeberu
'this->users->deleted' => FALSE,
tak je vše v pořádku.
Dokonce aji když odeberu ten limitBy
++ v mapperu nepoužívám žádná rozšíření
+++ db driver: mysqli
Díky za jakoukoliv pomoc.
Editoval Barbarossa (15. 10. 2018 14:37)
- hrach
- Člen | 1838
Ahoj, na Nette foru bych jiz rad nespamoval tematem Nextras Orm. Proto prosim, pokud mas otazku, problem, navrh, cokoliv:
- zaloz issue na GitHubu Nextras Orm – idealne anglicky, muzes cesky, issue muze byt otazka, nebo diskuze, whatever;
- zaloz issue na GitHubu Nextras Dbal – to stejne pro Dbal;
- prijd diskuzovat na Slack Pehaphari.cz – channel #nextras :)
Diky, hrach