Produkty Objednávky Zákazníci Dokumentace
Nepřihlášen Uživatel 1 Uživatel 2 Uživatel 3
Část III · Sloupce

10. Avatar

addAvatar($alias, $title, $filterOptions = null) – v datech je jen identifikátor osoby. Jméno, fotku, online stav i odkaz na profil si grid vyzvedne u UserProvideru, kterého má z DI.

Ukázka

Sloupec Id zákazníka vedle je tatáž hodnota bez presetu – v datech je opravdu jen číslo. Fotku má v demu jen půlka lidí, ostatní mají iniciály.

Zdrojový kód

php app/Modules/AdminModule/Components/Docs/Columns/ColumnAvatarGridControl/ColumnAvatarGridControl.php
<?php

declare(strict_types=1);

namespace App\Components\Docs\Columns\ColumnAvatarGridControl;

use Xart\Grid\Button\ButtonManager;
use Xart\Grid\Column\ColumnManager;
use Xart\Grid\Control\GridControl;
use Xart\Grid\DataOutput\Row;
use Xart\Grid\DataSource\DataSource;
use Xart\Grid\DataSource\SQL\SQLDataSource;

/**
 * Dokumentace, kapitola 10 – Avatar.
 *
 * V datech je jen id zákazníka; jméno, fotku, online stav i odkaz na profil dodá UserProvider.
 * Vedle je totéž id přes add(), aby bylo vidět, s čím sloupec doopravdy pracuje.
 */
class ColumnAvatarGridControl extends GridControl
{
    public function createDataSource(): DataSource
    {
        return new SQLDataSource(__DIR__ . '/ColumnAvatarGridControl.sql');
    }


    public function columns(ColumnManager $cm): void
    {
        $cm->add('number', 'Číslo');

        // Nabídka filtru [id => jméno]; bez ní by sloupec filtr neměl (textový by hledal v id).
        $cm->addAvatar('id_customer', 'Zákazník', $this->customerOptions());

        // Tatáž hodnota bez presetu – v buňce zůstane holé id.
        $cm->add('id_customer_raw', 'Id zákazníka');

        $cm->addNumber('total_price', 'Celkem', 2, 'Kč');
    }


    /**
     * Zákazníci, kteří mají aspoň jednu objednávku – nabízet ve filtru někoho, kdo v gridu není,
     * nemá smysl.
     *
     * @return array<string, string>
     */
    private function customerOptions(): array
    {
        $rows = $this->db->query(
            'SELECT c.id, CONCAT(c.first_name, " ", c.last_name) AS name
                FROM `customer` c
                WHERE EXISTS (SELECT 1 FROM `order` o WHERE o.id_customer = c.id)
                ORDER BY c.last_name, c.first_name',
        )->fetchAll();

        $options = [];
        foreach ($rows as $row) {
            $options[(string) $row->id] = (string) $row->name;
        }

        return $options;
    }


    public function buttons(ButtonManager $bm): void
    {
    }


    public function rows(Row $row): void
    {
    }


    public function render(): void
    {
        $this->template->render(__DIR__ . '/ColumnAvatarGridControl.latte');
    }
}
php app/Helpers/DemoUserProvider.php
<?php

declare(strict_types=1);

namespace App\Helpers;

use Nette\Application\LinkGenerator;
use Nette\Database\Explorer;
use Xart\Grid\User\UserDTO;
use Xart\Grid\User\UserProvider;


final class DemoUserProvider extends UserProvider
{
    public function __construct(
        private Explorer $db,
        private LinkGenerator $linkGenerator,
    ) {
    }


    protected function loadUser(string $id): ?UserDTO
    {
        $row = $this->db->query('SELECT * FROM customer WHERE id = ?', $id)->fetch();
        if ($row === null) {
            return null;
        }

        $numericId = (int) $id;

        return new UserDTO(
            $id,
            (string) $row->first_name,
            (string) $row->last_name,
            // Demo: fotku má jen půlka lidí, ať je vidět i vykreslení iniciál.
            $numericId % 2 === 0 ? 'https://i.pravatar.cc/256?u=' . urlencode($id) : null,
            null,
            $numericId % 4 === 0 ? $numericId % 3 === 0 : null,
            $numericId % 3 === 0 ? $this->linkGenerator->link('Customer:Form', [$id]) : null,
        );
    }
}

Cesta k avataru

  1. V buňce je identifikátor (id_customer).
  2. Grid se na něj zeptá UserProvideru – loadUser(string $id). Opakované dotazy na tutéž osobu si knihovna pamatuje, takže na stránku plnou objednávek jednoho zákazníka připadá jeden dotaz.
  3. Odpovědí je UserDTO, nebo null – pak zůstane buňka prázdná.
  4. Z UserDTO se vykreslí avatar se jménem.

Co UserDTO nese

id, jménoPovinné.
imageUrlBez ní se vykreslí iniciály na podkladu z náhradní palety – barvu určuje identifikátor, takže je pro touž osobu pokaždé stejná.
colorVlastní barva podkladu místo té z palety.
onlineZelená tečka u avataru.
profileUrlJméno se stane odkazem.

Filtr

Bez $filterOptions sloupec filtr nedostane – textový by hledal v syrových identifikátorech, což uživateli nepomůže. S nabídkou [id => jméno] z něj je vícenásobný výběr; klíče se zachovávají tak, jak přišly z databáze (id bývají čísla). V ukázce se nabízejí jen zákazníci, kteří aspoň jednu objednávku mají.

Na co si dát pozor

  • Řazení běží podle identifikátoru v databázi, ne podle jména – kdo chce řadit podle jména, musí je mít v dotazu (kapitola 3.3).
  • Prázdná buňka znamená, že řádek k žádné osobě nepatří; na UserProvider se grid v takovém případě neptá.
  • UserProvider je povinná služba celé knihovny, i když žádný avatar nemáte – viz kapitola 1.