Podpora databázového typu ENUM ve formulářích

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
horakmar
Člen | 16
+
0
-

Ahoj.
Jeden krátký dotaz. Má Nette nějakou podporu databázového typu ENUM? Resp. existuje nějaká best practise, jak s nimi pracovat? Já mám ve své aplikaci poměrně dost takových polí a řeším to zatím tak, že mám v modelu pole, které popisuje onen ENUM:

class EntryPresenter extends BaseRacePresenter {

	public static $start_options = [
			'none' => 'Nic',
			'early' => 'Brzy',
			'late'  => 'Pozdě',
			'red'   => 'Červená',
			'orange' => 'Oranžová'
		];
...

	public function createComponentEntryForm() {
		$form = new Form;

		...
		$form->addSelect('start_opt', 'Volby', self::$start_options);
		...
	}

	public function renderList($entryid = NULL) {
		$this->template->addFilter('startopt', function($s){
        	return self::$start_options[$s];
		});
		...
	}
}

A v šabloně potom:

	...
	<tr n:foreach="$entries as $entry">
	  <td>{$entry->start_opt|startopt}</td>
	...

Připadá mi to ale trochu těžkopádné a navíc duplikuju tím klíče toho ENUM – jednou v databázi, podruhé v kódu. Neexistuje nějaké lepší řešení?

Díky za odpověď,
Martin

greeny
Člen | 405
+
-3
-

já radši řeším enumy v db tak, že místo typu enum dám varchar a enum je to vlastně jen na straně PHPčka, většinou nějak takhle:

class UserStatus
{

	const REGISTERED = 'registered';
	const BANNED = 'banned';
	const VERIFIED = 'verified';
	// ...

	public static function isValid($stauts)
	{
		return in_array($status, self::getAll(), TRUE);
	}


	public static function getAll()
	{
		return array_keys(self::getPairs());
	}


	public static function getPairs()
	{
		return [
			self::REGISTERED => 'Registered',
			// ...
		];
	}

}

Všude kde hodnotu nastavuju vyhodím exception pokud mi neprojde isValid (např v Doctrině jen v setteru entity).

Funguje to krásně a když přidám další typ tak se nemusím zabývat migrací DB.

CZechBoY
Člen | 3608
+
0
-

My mame enumy ulozeny jako integery (min pameti to zere a asi se rychlejc hleda), protoze oracle nepodporuje enumy.
Mame to taky ulozeny jako static pole ve tride.

Tomáš Jacík
Člen | 147
+
0
-

@horakmar Pokud jde jen o klíč ⇒ hodnota, ukládám to pomocí konstant ve třídě entity a vytvořím si k tomu pole ve static proměnné, např. takto:

class Campaign
{
    const STATUS_ACTIVE = 'A',
        STATUS_STOPPED  = 'S',
        STATUS_DELETED  = 'D';

    public static $states = [
        self::STATUS_ACTIVE  => 'active',
        self::STATUS_STOPPED => 'stopped',
        self::STATUS_DELETED => 'deleted',
    ];
}

Do selectu to pak dostávám raději přes translator, abych měl v aplikaci jen anglické názvy:

foreach (Campaign::$states as $k => $v) {
    $this->statusOptions[$k] = $translator->translate("campaign.status.$v");
}

Není to však nutností, stačí selectu nasetovat to pole Campaign::$states.

Mnohem častěji ale potřebuji k těm stavům ukládat více informací. Např. barvu, kterou se zobrazí v datagridu nebo zda se má při změně na tento stav zaslat informační mail. Pak je lepší mít tabulku campaign_states a nacpat to vše do ní.

studna
Člen | 181
+
+1
-

Best practice je ENUM jako databázový typ nepoužívat. Co namísto toho použít obyčejný číselník?

Majkl578
Moderator | 1364
+
+1
-

Tomáš Jacík napsal(a):

class Campaign
{
    const STATUS_ACTIVE = 'A',
        STATUS_STOPPED  = 'S',
        STATUS_DELETED  = 'D';

    public static $states = [
        self::STATUS_ACTIVE  => 'active',
        self::STATUS_STOPPED => 'stopped',
        self::STATUS_DELETED => 'deleted',
    ];
}

Tohle bych definoval spíš trochu obecněji (hodnota konstanty stačí, netřeba další mapa překladu) a konstantou. Takhle třeba:

class Campaign
{
	const STATE_ACTIVE  = 1;
	const STATE_STOPPED = 2;
	const STATE_DELETED = 3;

	const STATES = [
		self::STATE_ACTIVE,
		self::STATE_STOPPED,
		self::STATE_DELETED,
	];
}

Jinak souhlasím s nepoužíváním enumu v databázi. Měnit strukturu databáze kvůli přidání nového stavu je tak trochu nic moc…

studna
Člen | 181
+
+1
-

Jen pozor na použití ENUM a číselných hodnot. Pokud v DB budeš mít ENUM('3', '2', '1') a vložíš hodnotu int(1), tak v DB budeš mít uloženou '3', nikoliv očekávanou jedničku.

Edit: Tady si nejsem jistý, jestli klíče ENUMu začínají nulou nebo jedničkou, podstatné je, že to lze velmi snadno přehlédnout a špatně používat.

$row = $row->update('status', Campaign::STATE_ACTIVE);
// ...
// tato podminka bude platit
$row->status == Campaign::STATE_DELETED;

Pro číselné hodnoty je lepší použít běžný integer a ENUM tak uměle držet pouze v kódu.

Tady jen na okraj pár dalších důvodů, proč se ENUM moc nehodí.

Editoval studna (5. 2. 2016 21:59)

horakmar
Člen | 16
+
0
-

Tohle bych definoval spíš trochu obecněji (hodnota konstanty stačí, netřeba další mapa překladu) a konstantou. Takhle třeba:

Nějaký překlad hodnot může být potřeba třeba pro jejich zobrazení v selectu.

horakmar
Člen | 16
+
0
-

Pro číselné hodnoty je lepší použít běžný integer a ENUM tak uměle držet pouze v kódu.

Tady jen na okraj pár dalších důvodů, proč se ENUM moc nehodí.

Myslím, že jste mě přesvědčili, že ENUM není dobrá věc a že bude lepší v DB použít buď INT, nebo číselník. V mém případě číselník nebude nutný. Také se zdá, že nápad se statickým polem hodnot v kódu nebyl úplně špatný.
Na ENUM se mi líbilo, že i při procházení databáze je vidět význam hodnot. Což se dá s trochu menším komfortem asi řešit poznámkou k tabulce.

Každopádně všem díky!
Martin

CZechBoY
Člen | 3608
+
0
-

Na co prochazet databazi rucne, kdyz pro to prave delas system? :-))

Tomáš Jacík
Člen | 147
+
0
-

@Majkl578 Nesouhlasím. Takto musíš to pole do selectu vždy vyrobit, nebo na to mít funkci. Mé řešení je imho mnohem jednoduší a ten výčet z něj vždy snadno uděláš přes array_keys.

horakmar
Člen | 16
+
0
-

CZechBoY napsal(a):

Na co prochazet databazi rucne, kdyz pro to prave delas system? :-))

Při ladění a úpravách. :-) Uživatel samozřejmě nebude data editovat ručně.