ublaboo/datagrid: mocný, rychlý, rozšiřitelný, hezký, anglicky dokumentovaný datagrid

matous.radek
Člen | 2
+
0
-

Ahoj lidi,
tak řešení zdá se býti naprosto jednoduché. Pomohlo mi až bádání přímo v kódu.

Tedy místo:

$grid->setItemsDetail($detail = TRUE, $primary_where_column = $grid->getPrimaryKey());

dát:

$grid->setItemsDetail($detail = TRUE, $primary_where_column = "table.column");

Editoval matous.radek (5. 11. 2016 11:19)

Pavel Janda
Člen | 977
+
0
-

@matous.radek Ahoj, nestihl jsem ti přes den odpovědět, ale již správné řešení znáš, takže thumbs up! Pokud to není v docu, napravím to.

JardaH
Člen | 5
+
0
-

Ahoj
Bylo, by možné aby funkce setEditableCallback měla jako parametr i původní hodnotu?
Potřeboval bych totiž zjistit, dotazem do databáze, jestli mi někdo hodnotu mezi zobrazením a zápisem do databáze nezměnil.
Děkuji

Pavel Janda
Člen | 977
+
0
-

@JardaH Ale původní hodnota, která by ti tam přišla, by byla stejně původní a ne vytažená z databáze… Tak si ji vytáhni. :)

JardaH
Člen | 5
+
0
-

@PavelJanda Z databáze si ji vytáhnu. Potřebuji ale původní hodnotu před editací(ta se už nemusí shodovat s hodnotou v databázi) a tu porovnám s hodnotou z databáze, jestli ji někdo jiný nezměnil. Snad se vyjadřuji srozumitelně.
Díky

ondrusu
Člen | 118
+
0
-

Ahojte, měl bych dotaz jak se dá v tomhle datagridu řadit podle cizího klíče?
Mám výpis článků a každý článek má svoji kategorii a já to chci seřadit podle kategorie.
Samozřejmě nejlépe podle názvu kategorie nebo podle idčka.
No ale nevím jak to mám udělat. V entitě Article mám kategorii definovanou takhle

/**
 * Sloupec Kategorie.
 * @ORM\ManyToOne(targetEntity="category", inversedBy="ac_category", fetch="EAGER")
 * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
 *
 */
protected $category;

A když definuji pouze

$grid->addColumnText('category', 'Kategorie')
     ->setSortable()

Tak to píše chybu že category_id neexistuje v dotazu

SELECT DISTINCT id_0
FROM (
  SELECT ... sloupce
  FROM category
) dctrn_result
ORDER BY c0_.category_id ASC
LIMIT 10 OFFSET 0

Ten dotaz je samozřejmě špatně, ale nechápu proč to rve až do toho co je za tím druhým FROM.
Můžete mi teda poradit jak to mám udělat správně?
Zkusil jsem metodu setSortableCallback (kterou jsem našel zde na nette foru), ale fakt asi dělám něco špatně. Protože nastal ten samý problém.
Děkuji.

Pavel Janda
Člen | 977
+
0
-

@ondrusu Ahoj.

Mohl bys sem dát i to, jak skládáš dotaz přes QueryBuilder? Pravděpodobně tam bude chybět join.

ondrusu
Člen | 118
+
0
-

Tak to viš, že tam chybí join, ten join už by měl být součástí entity né? Nebo ten datagrid si to nevezme z toho?
takhle vypadá ta metoda.

$qb = $this->em->createQueryBuilder();
$qb->select('a')
   ->from(Article::class, 'a');
return $qb;

A když tam přidám ten join tak např.
LEFT JOIN category c ON c.id = a.category_id
už bude blbě protože v entitě už to je jako array_collection
je to tak že?? Tak to si asi budu muset vytvořit ručně sql že??

Nebo se pletu a lze to jinak?? Díky.

Pavel Janda
Člen | 977
+
0
-

@ondrusu Nemáš to správně. Ten join píšeš na propetry entity, ne na sloupce v tabulce. Předpokládám, že žádné category_id v entitě nemáš.

Zkus něco jako LEFT JOIN category c ON c = a.categories.

ondrusu
Člen | 118
+
0
-

Pavel Janda napsal(a):

@ondrusu Nemáš to správně. Ten join píšeš na propetry entity, ne na sloupce v tabulce. Předpokládám, že žádné category_id v entitě nemáš.

Zkus něco jako LEFT JOIN category c ON c = a.categories.

díky, vyzkouším to a dám vědět (ale až večer)
Ale rozhodně to vypadá logicky.. díky

EDITOVÁNO 8.11. 21:25
funguje, díky
respekt++;

Editoval ondrusu (8. 11. 2016 21:24)

pitr82
Člen | 121
+
0
-

@PavelJanda
Všiml jsem si, jedné zvláštnosti.
$grid->setRememberState(FALSE);
$grid->setDefaultSort([‚create‘ ⇒ ‚DESC‘]);

Tak nejde třídit podle sloupce create.
Pokud použiju ASC, tak již jde.
Je to nějaká vlastnost?

Pavel Janda
Člen | 977
+
0
-

@pitr82 V master větvi je to již fixnuto. Příští verze (zanedlouho vydáváme) bude fix obsahovat. :)

pitr82
Člen | 121
+
0
-

@PavelJanda
pořád mi to nejde sortovat, když tam mám $grid->setRememberState(FALSE);
Bez toho to jde.
Jedu na masteru: dev-master 392d041

Pavel Janda
Člen | 977
+
0
-

@pitr82 Pardon, přehlédnul jsem to $grid->setRememberState(FALSE);. Bohužel v takovou chvíli nemá datagrid šanci zjistit, zda uživatel již sortoval nebo ne. Pravděpodobně v takové kombinaci začnu vyhazovat exception, pokud mě nenapadne nějaký spásný nápad.

Nebo máš nějaký?

pitr82
Člen | 121
+
0
-

@PavelJanda pokud použiju ASC, tak to jde

Editoval pitr82 (10. 11. 2016 10:56)

Pavel Janda
Člen | 977
+
0
-

@pitr82 Ano.

Pokud nastavíš default sort == DESC, tak další krok je NULL, pokud klikneš na sort, tak se v datagridu sort projeví jako NULL a to může znamenat buď kliknutí na DESC – další krok, nebo nic, žádné řazení. DataGrid si tedy řekne, že nic ještě nebylo řazeno a tak vrátí DESC, protože je to defaultní sort. Proto tam figuruje ta session – do ní se uloží, že již uživatel použil řazení a pokud ano, nebere se v potaz default sort.

Dává to smysl?

Editoval Pavel Janda (10. 11. 2016 11:49)

pitr82
Člen | 121
+
0
-

@PavelJanda Tak jedině pro $grid->setRememberState(FALSE); uložit do session přínzak , že si nemá nic pamatovat a podle čeho se defaultně sortuje. A při tom sortování zjistit, default sort, a pokud je DESC, tak další krot není nic, ale žádné řazení

flamengo
Člen | 131
+
0
-

Ahoj, snažím se přinutit datagrid, aby mi srovnal záznamy takto:

"...ORDER BY priority ASC, name ASC"

Tedy aby se zobrazilo dle nastavené priority a pokud je priorita stejná, tak abecedně dle názvu.

Očekával bych, že vyřeším takto:

$grid->setDefaultSort(['priority' => 'ASC', 'name' => 'ASC']);

Ale bohužel se mi to nedaří. Výsledek je řazen pouze dle name. Verzi mám v4.4.9.

Další dotaz: lze nějak nastavit i sloupci pro prioritu tento způsob řazení? Něco jako:

$tr = $grid->addColumnNumber('priority', 'Pořadí')
	->setSortable('priority ASC, name ASC')

Předem díky moc za odpověď či nakopnutí.

Pavel Janda
Člen | 977
+
+2
-

@flamengo Když počkáš ještě pár dní, tak vyjde verze 5, která bude obsahovat multisort. Zatím v dev-master.

libik
Člen | 96
+
0
-

Ahoj,

je nejaka moznost reloadnout datagrid (pripadne vicero, pokud na strance jsou) pres JS? V payloadu si vracim parametry, na jejichz zaklade bych reload potreboval provest.

Diky!

iguana007
Člen | 970
+
0
-

Ahoj, nedari se mi prijit na to, jak udelat vyhledavani nad sloupci non-case-sensitive, kdyz pouzivam jako datasource Doctrine, muzete mi prosim nekdo poradit, co s tim?

Pavel Janda
Člen | 977
+
0
-

@iguana007 Jakou používáš DB? A jaké používáš collation v tom kterém sloupci? Například utf8_general_ci bude Case-Insensitive, zato utf8_bin bude Case-Sensitive.

ondrusu
Člen | 118
+
0
-

Ahojte, měl bych dotaz. Definuji pomocí datagridu date sloupec a chci na něj filter.
Soupec mám definovaný takto:

$grid->addColumnDateTime('created','Vytvořeno')
     ->setSortable()
     ->setFilterDate();

a v latte to mám takhle (je to kvůli formátu, ale třeba se to dá udělat jinak

{define col-created}
{$item->getCreated()|date:'%d.%m.%Y %H:%M:%S'}
{/define}

A když kliknu do toho políčka tak by se teoreticky měl zobrazit takový kalendář pod tím labelem a to se nezobrazí, ani žádná chyba v conzili není.
Nevíte co mám nastaveno špatně??

Díky.

iguana007
Člen | 970
+
0
-

Pavel Janda napsal(a):

@iguana007 Jakou používáš DB? A jaké používáš collation v tom kterém sloupci? Například utf8_general_ci bude Case-Insensitive, zato utf8_bin bude Case-Sensitive.

Používám PostgreSQL, tudiž pomocí collation to neudělám :(

abc
Člen | 92
+
0
-

@iguana007 normálně převedeš ten string na lowercase v DB dotazu i parametr
Nevím, jak se to dělá v tomhle gridu, ale v Grido cca takto (určitě tam jsou chyby, ale návod to snad dodá):

->setWhere(function($value, $qb){
	return $qb->addAndWhere("LOWER(column) = :param", ["param" => Strings::lower($value),]);
})
iguana007
Člen | 970
+
0
-

abc napsal(a):

@iguana007 normálně převedeš ten string na lowercase v DB dotazu i parametr
Nevím, jak se to dělá v tomhle gridu, ale v Grido cca takto (určitě tam jsou chyby, ale návod to snad dodá):

->setWhere(function($value, $qb){
	return $qb->addAndWhere("LOWER(column) = :param", ["param" => Strings::lower($value),]);
})

tomu rozumim, vim jak to udelat pri klasickem dotazu, ale nevim, jak toto zakomponovat do datagridu – k cemu pripojim to volani ->setWhere atd., k datasource? Takto mám definici datagridu nyní:

		$grid = new DataGrid($this, $name);
		$userRepository = $this->getEm()->getRepository(User::CLASSNAME);
		$dataSource = $userRepository->createQueryBuilder('er');
		$dataSource->leftJoin('er.supplier', 'supplier');

		$grid->setDataSource($dataSource);
		$grid->addColumnText('username', 'Username')
			->setSortable('username')
			->setFilterText(['er.username']);
		$grid->addColumnLink('fname', 'First Name', 'detail')
			->setSortable('fname')
			->setFilterText(['er.fname']);
		$grid->addColumnLink('sname', 'Last Name', 'detail')
			->setSortable('sname')
			->setFilterText(['er.sname']);
		$grid->addColumnLink('supplier', 'Supplier', 'Suppliers:detail', 'supplier.name', ['id' => 'supplier.id'])
			->setSortable()
			->setOpenInNewTab(true);
		$grid->addFilterText('supplier', 'Supplier', 'supplier.name');
abc
Člen | 92
+
+2
-

@iguana007 v dokumentaci (ten grid fakt neznám a nikdy jsem ho nepoužil) píšou toto, tak asi tam…

$grid->addFilterText('custom', 'Custom search:', 'name')
	->setCondition(function($fluent, $value) {
		/**
		 * The data source is here DibiFluent
		 * No matter what data source you are using,
		 * prepared data source will be passed as the first parameter of your callback function
		 */
		$fluent->where('id > ?', strlen($value));
	});

http://ublaboo.org/datagrid/filter

iguana007
Člen | 970
+
0
-

Děkuji, toto jsem přehlédl

ondrusu
Člen | 118
+
0
-

Ahojte, připomínám se se svým dotazem (viz výše) a také mám dvě připomínky

Teď jsem aktualizoval všechny komponenty pomocí composeru a datagrid mi hlásí:

Trait 'Nette\SmartObject' not found search►
vendor\ublaboo\datagrid\src\DataModel.php:29

když to zakomentuju tak další chyba:

Call to undefined method Ublaboo\DataGrid\DataModel::onBeforeFilter() search►
vendor\ublaboo\datagrid\src\DataModel.php:132

když to zakomentuju, další chyba:

Call to undefined method Ublaboo\DataGrid\DataModel::onAfterFilter() search►
vendor\ublaboo\datagrid\src\DataModel.php:136
Call to undefined method Ublaboo\DataGrid\DataModel::onAfterPaginated() search►
vendor\ublaboo\datagrid\src\DataModel.php:150

Používám Nette 2.4.

  • tam není na tlačítku per_page_submit ve value nic.

pak se to zdá být ok.

Co to jsou za chyby? Je to něco, co bude oprava na dlouho?

Pavel Janda
Člen | 977
+
0
-

@ondrusu

1, Nezakázal jsi si nějak nette v composeru? Můžeš sem hodit composer.lock?
2, Upraveno zatím v masteru!

ondrusu
Člen | 118
+
0
-

Možné to je.

Zde je soubor.

Příznám se že jsem sám docela překvapen, hlavně celý projekt mám v gitu a teď jsem zjistil, že v .gitignore je složka /vendor (v .gitignore v root projektu), takže nemám ani zálohu v repositáři, abych to vrátil zpět.

Pavel Janda
Člen | 977
+
0
-

@ondrusu Pokud pro to nemáš nějaký superdůvod, je vhodné přidávat do .gitignore adresář vendor. Na cílové mašině pak jen zavoláš composer install.

Nemůže to být tím? Nehází ti tu exception tracy na jiném kompu, než tvém?

ondrusu
Člen | 118
+
0
-

Super důvod nemám, ale moc tomu nerozumim proč to tak je. Ale to je asi jedno. Nee hlásí to na mém pc na kterém jsem dal composer update
tak ten composer.lock je OK?

Editoval ondrusu (20. 11. 2016 19:40)

Pavel Janda
Člen | 977
+
0
-

@ondrusu Aha,ty používáš nette 2.3. My fault, změním použití traity Nette\SmartObject na extend třídy Nette\Object.

ondrusu
Člen | 118
+
0
-

@Pavel Janda to asi kvůli mě nemusíš, já jsem dal composer update a stahovalo to i nějaké věci z Nette 2.4.
tak jsem měl za to že mám Nette 2.4.
Takže navrhuješ aktualizaci Nette – stáhnout uplně znovu a překopírovat třídy atd?

Pavel Janda
Člen | 977
+
0
-

@ondrusu Ne. ublaboo/datagrid musí fungovat i s nette 2.3. Zkus master, pushnul jsem tam úpravu, mělo by to jet.

ondrusu
Člen | 118
+
0
-

To je zajímavý. Dneska jsem na jiným PC, nainstaloval jsem si composer (protože jsem ho tu neměl) přes flashku jsem si přetáhl ten adresář. Teď jsem dal zase composer update a vypsalo mi to tohle .
Ale včera to tohle nepsalo. Tak nevim co dělám špatně.
Teď to tedy funguje. Díky moc i za dost rychlou opravu.

iguana007
Člen | 970
+
+1
-

Pro referenci zde prikladam finalni/funkcni reseni case insensitive filtrovani s vyuzitim Doctrine + PostgreSQL:

		$grid = new DataGrid($this, $name);
		$userRepository = $this->getEm()->getRepository(User::CLASSNAME);
		$dataSource = $userRepository->createQueryBuilder('er');
		$dataSource->leftJoin('er.supplier', 'supplier');

		$grid->setDataSource($dataSource);
		$grid->addColumnText('username', 'Username')
			->setSortable('username');
		$grid->addColumnLink('fname', 'First Name', 'detail')
			->setSortable('fname');
		$grid->addColumnLink('sname', 'Last Name', 'detail')
			->setSortable('sname');
		$grid->addColumnLink('supplier', 'Supplier', 'Suppliers:detail', 'supplier.name', ['id' => 'supplier.id'])
			->setSortable()
			->setOpenInNewTab(true);

		$grid->addFilterText('username', 'First Name:', 'er.username')
			->setCondition(function ($dataSource, $value) {
				$dataSource->andWhere("LOWER(er.username) LIKE :param")
					->setParameter('param', '%' . Strings::lower($value) . '%');
			});
		$grid->addFilterText('fname', 'First Name:', 'er.fname')
			->setCondition(function ($dataSource, $value) {
				$dataSource->andWhere("LOWER(er.fname) LIKE :param")
					->setParameter('param', '%' . Strings::lower($value) . '%');
			});
		$grid->addFilterText('sname', 'Last Name:', 'er.sname')
			->setCondition(function ($dataSource, $value) {
				$dataSource->andWhere("LOWER(er.sname) LIKE :param")
					->setParameter('param', '%' . Strings::lower($value) . '%');
			});
		$grid->addFilterText('supplier', 'Supplier:', 'supplier.name')
			->setCondition(function ($dataSource, $value) {
				$dataSource->andWhere("LOWER(supplier.name) LIKE :param")
					->setParameter('param', '%' . Strings::lower($value) . '%');
			});
ondrusu
Člen | 118
+
0
-

Zdravím, mám ještě dotaz.
Mám v presenteru renderShow s id (url adresa /show/id)

public function renderShow($id) {
  $this->template->id = $id;
  ...
}

A v createComponent pro datagrid mám toto

public function createComponentList($name) {
$grid = new DataGrid($this, $name);
$grid->setDataSource(
  $this->facade->getList($this->template->id)
);

Základní výpis funguje, ale jak dám seřadit podle jména nebo něco vyhledat tak už v template neexistuje proměnná id a samozřejmě skončí to chybou.
Jak bych si měl správně předat ID z view (latte) do componenty?

díky moc.

Pavel Janda
Člen | 977
+
0
-

@ondrusu Zkusil bych nastavit id presenteru jako persistentní parametr. Používáš ajaxový datagrid?

ondrusu
Člen | 118
+
0
-

Ne myslím že ne.
To se dá nastavit nějak takhle?

   /** @persistent */
   public $id = 0;

 public function renderShow(id) {
   $this->id = id;
 }

public function componentList() {
 $grid->setDataSource(
   $this->facade->getList($this->id)
 );
}
Pavel Janda
Člen | 977
+
0
-

@ondrusu Stačí takto:

/** @persistent */
public $id = 0;

public function componentList() {
	$grid->setDataSource(
		$this->facade->getList($this->id)
	);
}

Persistentní parametry se pak ale umí chovat i trochu záludně, tak je s tím pak třeba počítat (když půjdeš na jinou stránku, to ID ti zůstane automaticky v linku).

ondrusu
Člen | 118
+
0
-

Pavel Janda napsal(a):

@ondrusu Stačí takto:

/** @persistent */
public $id = 0;

public function componentList() {
	$grid->setDataSource(
		$this->facade->getList($this->id)
	);
}

Persistentní parametry se pak ale umí chovat i trochu záludně, tak je s tím pak třeba počítat (když půjdeš na jinou stránku, to ID ti zůstane automaticky v linku).

díky, vyzkouším.

BuGeR
Člen | 45
+
0
-

Ahoj,

při používání datagridu s doctrine query builderem jsem narazil na problém. Potřebuji vytvořit statistickou tabulku, ve které zobrazím přehled uživatelů s doplňujícími informaceni, které jsou i ve vedlejších tabulkách.
Nenašel jsem žádný způsob, jak např. natáhnout do qb něco jako sum(t.order_amount) AS soucet_objednavek do gridu. Proto se snažím vytvořit abstraktní entitu, kterou použiju jako přepravku pro data. Takže potom ten qb vypadá takto:

$qb->select('NEW AbstractUser(u.id, u.email, SUM(t.order_amount))');

Všechno funguje v pořádku. Vrátí se mi entita, která obsahuje potřebné informace, které jsou najoinované z externích tabulek. Bohužel si s tím ale DataGrid neumí poradit. Vyhazuje to exception:
Not all identifier properties can be found in the ResultSetMapping: id
Nevíte někdo co s tím? Nebo neexistuje nějaký lepší a jednodušší způsob, jak natáhnout data z externích tabulek do gridu?

Díky.

Pavel Janda
Člen | 977
+
0
-

@BuGeR Ahoj,

bohužel nepoužívám tolik doctrine, ale zeptám se kolegy.

Co ale určitě bude fungovat, bude lazy načítání entit při výpisu dat. Používám to třeba u gridu objednávek. Pokud otevřu detail objednávky, donačtou se (lazy)
položky objednávky..

/**
 * @ORM\OneToMany(targetEntity="OrderItem", mappedBy="order", fetch="EXTRA_LAZY")
 */
protected $lasyOrderItems;

Pak v detailu položky (v šabloně) normálně nad položkami iteruji. Určitě bude existovat podobný způsob zapsání nějaké agregace. Ale to musí poradit někdo jiný.

Pokud bys ale toužil čistě po agregaci, tohle vhodný způsob nebude. Říkám, ještě se zeptám, pokud tu někdo nenapoví dříve.

esorimer
Člen | 114
+
0
-

iguana007 napsal(a):

Pro referenci zde prikladam finalni/funkcni reseni case insensitive filtrovani s vyuzitim Doctrine + PostgreSQL:

...

Hezké řešení, ale s postgresem bych si dovolil navrhnout používat ILIKE místo LIKE …

iNviNho
Člen | 352
+
0
-

Chalani nenarazili ste niekto na problém sortovania podľa joinnutej entity?

Mám v gride

<?php
$grid->addColumnText("invoiceNumericalScheme.companyName", "Názov firmy")
                ->setSortable();
?>

Po stlačení tlačídla sort mi vyhodí v konzoli error a následne ked refreshnem stránku tak dostávam chybu a problém je v DQLku

<?php
SELECT i
FROM InvoiceModule\Entities\Invoice i
ORDER BY invoiceNumericalScheme.companyName ASC
?>

chýba tam left join/inner join …

Nie je to bug? Ak sortujem podľa joinnutej entity tak by tam mal byť check či naozaj je v QueryBuildery táto entita joinnutá a ak nie je, čo je moj prípad, tak by tam mal ešte byť inner join

Editoval iNviNho (1. 12. 2016 18:50)

Pavel Janda
Člen | 977
+
+4
-

Nová verze – v5.0.0


kompletní changelog: https://github.com/…s/tag/v5.0.0

Díky všem za reporty a PR!

Editoval Pavel Janda (4. 12. 2016 13:47)

iNviNho
Člen | 352
+
0
-

@PavelJanda na stránke http://ublaboo.org/datagrid/column?… dostávam Server error 500 ked som zoradil podľa name, potom podľa ID a už bol error.

Ivorius
Nette Blogger | 119
+
0
-

Nebylo by možné vybírat rozsah checkboxů za pomocí shiftu? Jakože kliknu na 2. checkbox podržím shift a kliknu třeba 5. a budu mít vybrané checkboxy 2 – 5?