PHP DateTime & Locale

O objeto nativo do PHP DateTime não suporta internacionalização, ou seja, quando se executa o método $dateTime->format(), não existe uma maneira de se trazer o formato do idioma definido no servidor, feito no PHP com a função nativa setlocale.

Quando é necessária a internacionalização de datas uma alternativa é utilizar a função nativa do PHP strftime, a qual recebe como parametros o formato e o timestamp. Para substituir o format do DateTime, mais uma coisa deve ser levada em consideração: a sintaxe da string do formato é diferente. O format do DateTime utiliza a sintaxe definida pelo objeto nativo do PHP Date, já o strftime utiliza a sintaxe definida no padrão UNIX.

Lembrando que para o setlocale funcionar, o servidor onde o PHP está instalado, deve ter esse idioma configurado.

Para consultar os idiomas em um servidor de sistema operacional base UNIX execute o comando no terminal:

$ locale -a

Caso você queira configurar um idioma que não esteja na lista como por exemplo “pt_BR” execute a seguinte comando no terminal:

$ sudo locale-gen ru_pt_BR

Tendo o idioma configurado, você pode executar a função setlocale no PHP, sendo que o primeiro parâmetro é a categoria, que no nosso caso pode ser LC_ALL ou LC_TIME, e o segundo parâmetro o idioma que no nosso caso é “pt_BR”.

Concluindo, o DateTime não tem a capacidade de formatar com internacionalização mas é possível utilizar outros recursos do PHP e se for o caso, criar uma classe que substitua o DateTime. Essa classe pode herdar a classe DateTime e sobrescrever os métodos de maneira adequada. Ou essa classe pode apenas ter como atributo um objeto do tipo DateTime e implementar os métodos que forem necessários. Segue abaixo um exemplo do segundo caso:

<?php
namespace Date;

class Date
{
    private $dateTime;

    public function __construct($time = null, $object = null, $format = null, $timestamp = null)
    {
        if ($time === null && $object === null && $format && $timestamp) {
            $dateTime = \DateTime::createFromFormat($format, $timestamp);
            if ($dateTime instanceof \DateTime) {
                $this->dateTime = new \DateTime($time, $object);
                $this->dateTime->setTimestamp($dateTime->getTimestamp());
            }
        } else {
            $this->dateTime = new \DateTime($time, $object);
        }
    }

    public function format($format, $locale = false)
    {
        if ($locale) {
            setlocale(LC_TIME, $locale);
            return strftime($format, $this->dateTime->getTimestamp());
        }

        return $this->dateTime->format($format);
    }

    public function getTimestamp()
    {
        return $this->dateTime->getTimestamp();
    }

    public function setTimestamp($param)
    {
        $this->dateTime->setTimestamp($param);
    }

    public function getDateTime()
    {
        return $this->dateTime;
    }
}

Query customizada com TableGateway

Para começar uma aplicação  que faz acesso a banco de dados utilizando o Zend Framework 2, a maneira mais prática de se executar as queries seria utilizando a classe do componente do ZF2 DB chamada TableGateway.

Veja um exemplo instanciando a classe:

<?php

use Zend\Db\TableGateway\TableGateway;
use Zend\Stdlib\Hydrator\ClassMethods as Hydrator;
use Zend\Db\ResultSet\HydratingResultSet;

$hydratingResultset = new HydratingResultSet(new Hydrator(), new Aluno());

$adapter = $serviceLocator->get('Zend\Db\Adapter\Adapter');
$tableGateway = new TableGateway(

    'aluno',

    $adapter,

    null,

    $hydratingResultset

);

Em seu construtor o TableGateway recebe 4 parâmetros. O primeiro é o nome da tabela no banco de dados que será modificada ou consultada que no exemplo é ‘aluno’.

O segundo parâmetro é o adapter que no caso estou obtendo o serviço que está no service locator, imaginando que tenho essa variável preenchida adequadamente. Veja abaixo como posso configurar o molulo para pegar esse serviço de maneira que as configurações de banco de dados venham das definidas do module.config.php, basta editar o module.config.php para que o serviço ‘Zend\Db\Adapter\Adapter’ sejá criado pela factory de adapter do próprio zend:

'service_manager' => array(
    'factories' => array(

        'Zend\Db\Adapter\Adapter' =>
            'Zend\Db\Adapter\AdapterServiceFactory',

...

O terceiro parâmetro são as features do TableGateway, neste exemplo não utilizaremos nenhuma, portanto passamos NULL.

O quarto parâmetro é o HydratingResultSet que é o cara que o TableGateway vai utilizar para hidratar ou extrair o objeto relacionado à essa tabela. Para instanciar o HydratingResultSet é necessário passar por parâmetro um Hydrator e um objeto que será hidratado ou extraído. Como primeiro parâmetro estou utilizando o objeto que estou apelidando de Hydrator no momento em que faço o use, esse objeto na é verdade é o ClassMethod. Esse Hydrator utiliza os métodos get do objeto a ser extraído para gerar o array e utiliza os métodos set para hidratar (preencher o objeto) com os valores do array. Como segundo parâmetro estou passando um objeto Aluno imaginando que a classe contém get e set de seus atributos e que seus nomes são compatíveis como os nomes dos campos no banco de dados.

Agora que temos o objeto TableGateway, podemos executar as querys.

Os métodos mais usados são:

  • select
  • insert
  • update
  • delete

No caso do insert, update, delete recebem como parâmetro um array como os dados das linhas a serem modificadas, o select recebe por parâmetro um array como os campos que irão aparecer na clausula where da query.

O ponto chave dessa postagem é mostrar como alterar essa query para fazer consultas mais complexas. Porque até agora conseguimos fazer uma consulta de maneira bem prática, mas na query que executamos só conseguimos modificar a clausula where, mas e se quiser alterar a ordenação por exemplo?

A função select também pode receber por parâmetro uma função anônima (closure). Essa função recebe por parâmetro um objeto do tipo Zend\Db\Sql\Select com o qual é possível ter uma alcance maior de possibilidades de query.

Sendo assim para definir a ordenação de um consultado podemos fazer algo como:

use Zend\Db\Sql\Select;

$orderAsc = true;

$resultado = $tableGateway->select(
    function (Select $select) use ($orderAsc) {
        if ($orderAsc) {
            $select->order('name ASC');
        } else {
            $select->order('name DESC');
        }
    }
);

Nesse exemplo estou ordem por nome em ordem alfabética crescente se eu mudar o $orderAsc para false será em ordem decrescente, como permitido pela closure no PHP posso importar uma variável que está fora da função com a keyword use.