# Codestyle

  • názvy proměnných, metod jako camelCase
  • názvy tříd, interfaců, trait jako UpperCamelCase
  • datové typy
    • v anotaci @var používat jen php typy. Např. integer není php typ, je potřeba int
    • naopak v doctrine je potřeba psát integer
    • do třetice, JMS Type umí jak int tak integer
    • pokud možno nepsat typ array, ale být konkrétnější, třeba Order[]

# Formát konstrukcí

  • nenechávat zbytečně dva prázdné řádky za sebou
  • složené závorky se píšou na nový řádek u konstrukcí metod, tříd, interfaců a trait, jinak na stejný řádek. Ukázka:
class GreatClass
{
    private int $value;

    public function updateValue(int $value): void
    {
        if ($value < 0) {
            $this->value = -1;
        } else if ($value === 0) {
            $this->value = 0;
        } else {
            $this->value = 1;
        }

        /** @var int[] $items */ // only if necessary
        $items = [1, 2, 3];
        foreach ($items as $item) {
            // do things
        }
        
        try {
            // something that throws exception
        } catch (\Exception $exception) {
            throw $exception;
        }
    }
}

# Metody

Konstruktor typicky nepotřebuje blok s anotacemi, nemá žádnou přidanou hodnotu. Mezi datovým typem a názvem parametru dát jednu mezeru, nezarovnávat více mezerami - nutno přenastavit v IDE- Editor / Code Style / PHP / Wrapping and Braces / Function declaration parameters / Align when multiline, viz https://youtrack.jetbrains.com/issue/WI-61799. Používáme trailing comma za seznamem parametrů.

Ukázka pro PHP 8.0+:

    public function __construct(
        private UserControllerFacade $userFacade,
        private ApiExceptionFactory $apiExceptionFactory,
    )
    {
    }

# Type cast

  • psát mezeru za typecastem:
(int) $someNumber

# Jak psát return

  • psát newline před return, když není jediným příkazem v bloku:
public function getNumber(): int
{
    return 123;
}

public function getAnotherNumber(bool $largeNumber): int
{
    if ($largeNumber) {
        return 6000;
    }
    
    return 6;
}

# Anotace

  • před anotacemi psát prázdný řádek, ukázka:
$a = 123;

/** @var Collection|Order[] */
$orders = new ArrayCollection();

# Priority

Ve třídě mají jednotlivé části své pořadí:

  1. use trait
  2. konstanty v pořadí public, protected, private
  3. atributy v pořadí public, protected, private
  4. static metody
  5. konstruktor
  6. metody v pořadí public, protected, private

V testech ale pro přehlednost píšeme public metodu testAbc a hned za ni private metodu provideAbcData.

# Guard Clause

Guard clause je zápis podmínek, který zvyšuje čitelnost a snižuje hloubku zanoření ifů. Jde vlastně o to otočit logiku ifu tak, aby se zavolal return co nejdřív to jde.

Ukázka:

bez guard clause:

if ($a !== null) {
    if ($a->getB() !== null) {
        // some heavy logic with $a->getB()->getValue()
        return $a->getB()->getValue();
    }
}

return null;

guard clause:

if ($a === null) {
    return null;
}

if ($a->getB() === null) {
    return null;
}

// some heavy logic with $a->getB()->getValue()
return $a->getB()->getValue();

# Refy a DTO

Historicky pro datové obálky pro api vstup a výstup z controlleru používáme označení ref, třeba UserRef.

Pro zanořené refy máme konvenci pro umístění souboru. Když se v refu KioskRef objeví zanořený field s názvem location, tak by se zanořený ref dal do složky ApiRef/Location/LocationRef. Díky tomu by se pak případně mohlo zanořovat i do LocationRefu, kdyby bylo potřeba. Kdyby bylo potřeba do LocationRefu dát, pro ilustraci třeba políčko gps, tak by se dal ref do složky ApiRef/Location/Gps/GpsRef atd. Hlubší zanoření se hodí třeba v reportech.

Navíc díky téhle struktuře je na první pohled vidět, který ref je "vstupní bod".

# Naming

  • Controllery pojmenováváme s entitou v jednotném čísle, např. StreamController
  • url routy používá množná čísla jako v REST, např. seznam uživatelů pro klub - /admin/clubs/{clubId}/users
  • route name je snake_case (nechceme pomlčky, dělají problémy na FE - json klíče s pomlčkami, uff...)

# Ostatní

  • Kde to jde, tak používat ostré porovnání $a === $b se třemi rovnítky. Rozhodně nechceme spoléhat na automagický převod datových typů při použití pouze dvou rovnítek. U testů chceme používat assertSame (tj. ===), pouze pro float používat assertEquals (==).

Pokud možno nepoužívat new \DateTime() přímo, ale službu CurrentDateProvider - je to kvůli testům, které mohou být zafixované na konkrétní datum.