Skip to content

Instantly share code, notes, and snippets.

@jmurowaniecki
Last active April 15, 2020 12:00
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jmurowaniecki/bb8ea9334c6e9b673847 to your computer and use it in GitHub Desktop.
Save jmurowaniecki/bb8ea9334c6e9b673847 to your computer and use it in GitHub Desktop.
Modelo de README para projetos que utilizam CodeIgniter, MySQL, MongoDB, ..

Sumário

  1. Padrões
    1. Code standart
    2. Codificação de caracteres, arquivos e pastas
    3. Versionamento
      1. Dicas
        1. Para incluir ou remover arquivos do versionamento
  2. Preparando o ambiente
  3. Configuração do ambiente.
    1. Configurando dependências
      1. MongoDB
      2. Composer
      3. NodeJS, npm, grunt
    2. Configurando o arquivo de Hosts
    3. Configurando o Apache2
    4. Criando e configurando o banco de dados MySQL
  4. Estrutura inicial do projeto com CodeIgniter
    1. Configurações do CodeIgniter
  5. Testes unitários
    1. Codeception
      1. Elaborando testes unitários com Codeception
  6. Aumento de performance
    1. Dicas de performance
  7. Resolvendo problemas
    1. O endereço project.dev não funciona
    2. A Database Error Occurred
      1. Table 'project.ci_session' doesn't exist
  8. Desenvolvimento
    1. CRUD
      1. Modelagem e controle
      2. Usuários e gerenciamento de acesso
    2. Integração com template
      1. Paleta de cores
  9. Geradores de documentação
    1. phpDox
      1. Instalação e configuração
      2. Execução e visualização
      3. Problemas
        1. ext/xsl not installed/enabled
    2. phpLoc
      1. instalação
    3. APIGen
      1. Instalação e configuração
      2. Execução e visualização
      3. Dicas de formatação dos comentários
    4. phpDocumentor
      1. Instalação e configuração
      2. Execução e visualização
    5. Smart Comments
    6. JSDocs
    7. Script de documentação
  10. API ProjectCRUD
    1. Requisições / Abertura de ticket
      1. Configurando Controller/Model
      2. Efetuando requisições
        1. Listagem e aquisição de dados
        2. Inserção e atualização de dados
        3. Deleção
        4. Gerenciando tickets
      3. Manipulando resultados
      4. Efetuando requisições via Javascript
  11. Aplicação de tema Bootstrap

Padrões

Code standart

Afim de manter o código visualmente agradável e simples utilize preferencialmente os padrões estabelecidos em FIG/PSR-2.

Pontos importantes antes de começar:

  • Sempre utilize 4 (quatro) espaços como tabulação;
  • Jamais utilize tabulação mista de espaço com tabs;
  • Utilize um espaço entre o if e a condição ( .. ) e abertura do seu respectivo bloco. Exemplo: if ($numero > 1) {;
  • Utilize um espaço entre o switch e a condição ( .. ). Exemplo switch ($numero) {;
  • Utilize um espaço entre o for e a condição ( ..; ..; ). Exemplo for ($i = 0; $i < 10; $i++) {;
  • Utilize um espaço entre o foreach e a condição ( .. as .. ). Exemplo foreach ($colecao as $elemento) {;
  • Utilize um espaço entre o while e a condição ( .. ). Exemplo while ($i) {;
  • Utilize um espaço entre o function e o nome da função, não há espaço entre o nome da função e seus argumentos ( .., .. ). Exemplo function teste($i);
  • Utilize um espaço entre o class e nome da classe;
  • Utilize boas práticas para montar suas condições e encadear seus operadores booleanos, para mais informações veja a documentação no PHP.net;
<?php
/**
* SEMPRE DOCUMENTE SEUS ARQUIVOS
*
* Note que para que seja gerada documentação correta as marcações devem começar sempre com uma barra e dois asteríscos /**.
*
* @author John Murowaniecki <jmurowaniecki@gmail.com>
* @package Exemplo
*/

/**
* Documentação da classe Variaveis_Comuns
* @package Exemplo
*/
class Variaveis_Comuns
{
    // Quando declarar uma classe utilize a abertura do seu bloco na linha seguinte na mesma coluna da classe.

    /**
    * @var string|boolean|mixed $variavel_de_escopo Exemplo de declaração de variável em uma classe.
    */
    var $variavel_de_escopo = "variável de escopo";
    // Utilize nomes de variáveis fáceis de memorizar e entender afim de tornar desnecessário centenas de linhas de
    // código para explicar que tipo de informação aquela variável deveria conter.
}

/**
* Documentação da classe Teste que extende a classe Variaveis_Comuns
* @package Exemplo
*/
class Teste extends Variaveis_Comuns
{
    /**
    * Rotina que executa uma função passando dados de um array de argumentos.
    *
    * Essa rotina efetua uma varredura do parâmetro inicial, que deve ser um array e executa a função informada
    * recebendo como parâmetro o elemento e o índice da array informada.
    * O laço é desfeito no momento em que a função informada retornar false.
    *
    * Verifique nas funções de teste informadas as condições do retorno.
    *
    * @access public
    * @param string $titulo Recebe o título.
    * @param array $argumentos[] Recebe coleção de itens.
    * @param callable $funcao Recebe função a ser executada com itens da coleção.
    * @package Exemplo
    */
    public function executaFuncaoPassandoArgumentos($titulo = null, $argumentos = array(), $funcao = false)
    {
        // Blocos de função, assim como blocos de classes devem ter sua abertura na linha seguinte a sua declaração.
        // Sempre que possível declare sua visibilidade (public/private/protected).

        // Quando for necessário imprimir informações sem retornar quaisquer valor utilize a função `echo`.
        // Priorize sempre utilizar strings com áspas duplas "" e, sempre que possível, evite concatenar informações
        // como o seguinte exemplo: echo "teste"." de "."escrita".
        // Utilize {} afim de evidenciar o uso de variáveis populando a string conforme exemplo abaixo.
        // Certifique-se de que o valor contido na variável seja string ou possa ser convertido para string.
        if (is_string($titulo))
            echo "---\n{$titulo}:\n";
        // Você pode (mas não deve) utilizar condicionais sem declaração de abertura e fechamento de bloco se a execução
        // da condição for pequena, simples ou muito evidente, todavia prefira sempre declarar abertura e fechamento.

        if ((bool)$argumentos &&    // Verifica binariamente se argumentos é verdadeiro.
            $funcao &&              // Verifica se uma função foi informada.
            is_callable($funcao) && // Verifica se a função informada é executável.
            is_array($argumentos))  // Verifica se os argumentos são uma array válida.
        {
            // Blocos condicionais com múltiplos parâmetros, quando divididos em múltiplas linhas devem ter sua abertura
            // na linha seguinte a sua declaração, diferente de blocos de condição simples - veja exemplo abaixo.

            foreach ($argumentos as $i => $argumento) { // Percorre argumentos.
                // Blocos de laços foreach/for/while, devem ter sua abertura no fim da linha em que são declarados.

                if (!$funcao($argumento, $i)) { // Executa a função com argumento e índice como parâmetros.
                    // Blocos condicionais simples devem ser abertos na mesma linha em que são declarados.

                    break; // Caso o retorno seja falso para de percorrer argumentos.
                }
            }
        }
    }
}

/**
* Exemplo de callback utilizando uma função declarada normalmente.
*
* Primeiramente verifica se a função não foi previamente declarada (funções não devem ser redeclaradas).
*
* @param mixed $dado Recebe nodo.
* @param integer $dado Recebe índice do nodo.
* @package Exemplo
* @return bool Retorna verdadeiro ou falso conforme conteúdo do $dado. Se $dado for falso, retorna falso, senão retorna verdadeiro.
*/
if (!function_exists("exemplo_de_callback")) {
    // Blocos condicionais simples devem ter sua abertura na mesma linha em que são declarados.

    function exemplo_de_callback($dado = false, $indice = false)
    {
        // Utilize preferencialmente operadores ternários para efetuar operações simples.
        return ($dado                          // Verifica se dado é verdadeiro.
            ? print("\t{$indice} = {$dado}\n") // Imprime índice = dado.
            : false);                          // Retorna falso caso dado seja inválido, false, null ou 0.
    }
}

/**
* Exemplo de callback utilizando uma função anônima definida para uma variável.
*
* Verifica se o dado é binariamente válido e exibe dado recebido juntamente do respectivo índice.
*
* @param mixed $dado Recebe nodo.
* @param integer $dado Recebe índice do nodo.
* @package Exemplo
* @return true Retorna sempre verdadeiro
*/
$novo_exemplo_de_callback = function ($dado = false, $indice = false)
{
    // Em funções anônimas definidas em uma variável declare o bloco na linha seguinte.

    print "\t".((bool)$dado                              // Verifica se dado é binariamente verdadeiro
        ? "{$indice} significa {$dado}"                  // Imprime texto
        : "nenhum valor informado para {$indice}")."\n"; // Imprime texto

    return true; // retorna sempre true
};
// Não esqueça que após definir uma função anônima em uma variável é necessário finalizar o comando com ponto-e-vírgula.

// Array contendo os dados a serem validados.
$dados = array("alpha", "bravo", "charlie", "delta", 13, true, false, "não vai executar no primeiro exemplo");

// Instancia a classe Teste na variável $teste
$teste = new Teste();

// Executa o primeiro exemplo de callback declarado como uma função ordinária.
// Note que esse exemplo não processa a lista até o final, ela para quando recebe o valor false.
$teste->executaFuncaoPassandoArgumentos("Primeiro exemplo", $dados, "exemplo_de_callback");

// Executa o segundo exemplo de callback com função anônima definida para variável.
// Esse exemplo executa até o fim da lista.
$teste->executaFuncaoPassandoArgumentos("Segundo exemplo", $dados, $novo_exemplo_de_callback);

// Executa o terceiro exemplo de callback com função anônima definida para variável.
$teste->executaFuncaoPassandoArgumentos("Terceiro exemplo", $dados, function ($elemento = false, $indice = null) {
    // Funções anônimas definidas como parâmetros, argumentos ou conteúdo devem ter a abertura de seus blocos no fim da
    // linha em que foram declaradas.

    // A função print sempre retorna 1.
    return print("{$indice}: ".(is_string($elemento)
        ? "{$elemento}"
        : "informando valor {$elemento} não é uma string")."\n");
});
// O fechamento do bloco da função deve ser dado junto do fechamento dos parâmetros da função.

// O fechamento da tag php deve existir apenas se o arquivo não for um controller, model, library ou helper.
?>

Codificação de caracteres, arquivos e pastas

Salve seus arquivos e códigos sempre utilizando codificação UTF-8 (sem BOM), evite utilizar caracteres especiais fora da faixa de operação do mesmo - em dúvidas verifique na wiki e em grupos de discussão confiáveis.

A configuração do banco de dados MySQL deve ser utf8_unicode_ci. Veja na seção de configuração do banco de dados para mais detalhes.

Quanto a nomenclatura de arquivos e pastas tente utilizar todos minúsculos visto que muitas implementações serão realizadas em ambientes UNIX/Linux e virtualizações nas quais o sistema de arquivos é CASE SENSITIVE.

Relembrando:

  • pastas com texto em minúsculo;
  • arquivos de classes controllers, models, bibliotecas e helpers com primeiro caractere maiúsculo;
  • arquivos de views com nomes em minúsculo;
  • arquivos de estilo e javascriot em minúsculo;
  • nomes de arquivos gerados automaticamente minúsculos;

Versionamento

Utilize preferencialmente Github ou Bitbucket, não esqueça de efetuar seus commits regularmente especificando sempre que possível ao ID da tarefa no respectivo branch.

Dicas

Para incluir ou remover arquivos do versionamento

Utilize os comandos abaixo para incluir e excluir arquivos do versionamento. Esse comando é particularmente útil para ser aplicado em arquivos de configuração local, como .htaccess, wp-config.php, entre outros..

git update-index --assume-unchanged <file>

git update-index --no-assume-unchanged <file>

Preparando o ambiente

O ambiente de desenvolvimento deve rodar em http://project.dev/, para isso foram criados alguns procedimentos para auxiliar a configuração.

Afim de evitar configurações maiores em nameservers (BIND) o ambiente foi montado exclusivamente setando o arquivo /etc/hosts (ou caminho relativo em demais distribuições/SOs) apontando para localhost, sendo criado unicamente um virtual server nas configurações do Apache.

Configuração do ambiente.

Antes de mais nada é necessário ter instalado a versão mais recente estável do PHP (que atualmente é a 5.6.6, mas vamos nos manter momentaneamente pela 5.5 devido ao grande número de bibliotecas e frameworks otimizados exclusivamente pra essa versão).

sudo apt-get install php5 php5-cli php-pear mysql-server mysql-client libapache2-mod-php5 php5-mysql php5-curl php5-gd php5-intl php-pear php5-imagick php5-imap php5-mcrypt php5-memcache php5-ming php5-ps php5-pspell php5-recode php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl phpmyadmin

Configurando dependências

Conjunto de ferramentas implementado ao longo do desenvolvimento da aplicação.

MongoDB

sudo pecl install mongo

Segue abaixo uma mensagem de sucesso:

Build with Cyrus SASL (MongoDB Enterprise Authentication) support? [no] : no
You should add "extension=mongo.so" to php.ini

Obviamente você deve seguir a indicação do instalador e adicionar a linha extension=mongo.so em seu php.ini.

Composer

O Composer é um gerenciador de dependências para PHP que utilizaremos desde o início em nossos projetos. Para instalar ele globalmente (em todo o sistema) utilize os comandos abaixo:

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

Ao instalar o Composer utilizando os passos descritos acima o seguinte resultado será impresso em seu terminal:

john@stormtrooper101:/var/www$ curl -sS https://getcomposer.org/installer | php
#!/usr/bin/env php
All settings correct for using Composer
Downloading...

Composer successfully installed to: /var/www/composer.phar
Use it: php composer.phar
john@stormtrooper101:/var/www$ mv composer.phar /usr/local/bin/composer

Se por algum problema ou diretriz de segurança não for possível instalar o Composer globalmente ainda é possível sua instalação local curl -sS https://getcomposer.org/installer | php ou php -r "readfile('https://getcomposer.org/installer');" | php.

NodeJS, npm, grunt

Para instalar o grunt é necessário instalar o npm - que, por sua vez, é o gerenciador de pacotes do NodeJS. Utilize os comandos abaixo se estiver utilizando uma distribuição Linux baseada em Debian:

curl -sL https://deb.nodesource.com/setup | sudo bash -
sudo apt-get install -y build-essential nodejs

Configurando o arquivo de Hosts

É necessário alterar o arquivo /etc/hosts para que as requisições efetuadas no endereço http://project.dev/ sejam feitas localmente. Para isso, execute o comando abaixo como super usuário (leia-se sudo su), ele verificará se já existe uma entrada e, caso necessário, criará uma nova.

[ "`cat /etc/hosts|grep project`" ] || echo -e "127.0.0.1\tproject.dev" >> /etc/hosts

Configurando o Apache2

A configuração do Apache é basicamente criar uma entrada na pasta /etc/apache2/sites-available/, habilitar essa instância e reiniciar o processo do web server. Para isso execute os comandos abaixo como super usuário (sudo su):

name=project
n=0;e=echo;f=$"`$e 'PHs1fSAqOjgwPgoJU2VydmVyQWRtaW4gd2VibWFzdGVyQGxvY2FsaG9z
dAoJRG9jdW1lbnRSb290IHsyfXs5fS8KCVNlcnZlck5hbWUgezl9LmRldgoJPHsxfSAvPgoJCXs2
fSB7N30KCQl7NH0gQWxsCgk8L3sxfT4KCTx7MX0gezJ9ezl9Lz4KCQl7Nn0gSW5kZXhlcyB7N30g
TXVsdGlWaWV3cwoJCXs0fSBBbGwKCQlPcmRlciB7OH0sZGVueQoJCXs4fSBmcm9tIGFsbAoJPC97
MX0+CglFcnJvckxvZyAke3szfX0vezl9LWVycm9yezB9CglDdXN0b21Mb2cgJHt7M319L3s5fS1h
Y2Nlc3N7MH0gY29tYmluZWQKPC97NX0+'|base64 -d`"
for S in .log Directory '\/var\/www\/' APACHE_LOG_DIR AllowOverride VirtualHost Options FollowSymLinks allow $name
do f=$"`$e $"$f"|sed s/{$n}/$S/`";n=`expr $n + 1`;done;$e $"$f" > /etc/apache2/sites-available/$name
a2ensite $name
service apache2 reload

Para configuração manual do arquivo /etc/apache2/sites-available/project utilize o seguinte conteúdo:

<VirtualHost *:80>
    ServerAdmin john@contrata.eu

    DocumentRoot /var/www/project/
    ServerName project.dev

    <Directory />
        Options FollowSymLinks
        AllowOverride All
    </Directory>

    <Directory /var/www/project/>
        Options Indexes FollowSymLinks MultiViews
        AllowOverride All
        Order allow,deny
        allow from all
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/project-error.log

    CustomLog ${APACHE_LOG_DIR}/project-access.log combined
</VirtualHost>

Criando e configurando o banco de dados MySQL

Crie o banco de dados e a estrutura do arquivo de sessões do CodeIgniter através do seguinte comando:

senha="COLOQUE AQUI SUA SENHA DE ACESSO ROOT AO BANCO DE DADOS"
echo "CREATE DATABASE project;
USE project;
CREATE TABLE IF NOT EXISTS `ci_sessions` (
  `id` varchar(40) NOT NULL,
  `ip_address` varchar(45) NOT NULL,
  `timestamp` int(10) unsigned DEFAULT 0 NOT NULL,
  `data` blob DEFAULT '' NOT NULL,
  PRIMARY KEY (id),
  KEY `ci_sessions_timestamp` (`timestamp`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;"|mysql -uroot -p"$senha"

Estrutura inicial do projeto com CodeIgniter

Utilizaremos o framework CodeIgniter, para isso será necessário efetuar download do pacote do GitHub ou de outra fonte segura. O código abaixo efetua download e descompactação do repositório público do mesmo:

C=CodeIgniter;D=develop;wget https://github.com/bcit-ci/$C/archive/$D.zip;unzip $D;mv $C-$D /var/www/project;rm $D.zip

A documentação do CodeIgniter pode ser encontrada aqui.

Configurações do CodeIgniter

Basicamente é necessário setar apenas as configurações referentes a URL e dados de conexão com o banco de dados. O arquivo application/config/config.php deve conter a seguinte configuração:

$config['base_url']   = "http://$_SERVER['SERVER_NAME']/";
$config['index_page'] = '';

Para ganho de performance, melhoria da segurança e criação de logs substanciais utilize a seguinte configuração:

$config['log_threshold']   = 1;

$config['cookie_prefix']   = 'project';
$config['cookie_domain']   = $_SERVER['SERVER_NAME'];
$config['cookie_secure']   = TRUE;

$config['compress_output'] = TRUE;

Utilize uma string aleatória para gerar a chave de encriptação do CodeIgniter. Existem várias formas de gerar essa string, os seguintes comandos:

date +%s|sha256sum|md5sum
< /dev/urandom tr -dc _A-Z-a-z-0-9\$\=\+\- | head -c32;echo

Ambos comandos geram strings baseados na hora local, o retorno desse comando gera um texto similar ao abaixo, utilize o que melhor lhe convir:

f4276529116c35c180f33f66732f7fc6  -
3N6rtDTD_YQq6CcZUCu_aP0rsjHgntR_

Você deve configurar o CodeIgniter com esse dado no arquivo application/config/config.php da seguitne forma:

$config['encryption_key'] = '3N6rtDTD_YQq6CcZUCu_aP0rsjHgntR_';

Testes unitários

Codeception

Para informações sobre como criar e configurar testes unitários verifique documentação específica anexa.

Elaborando testes unitários com Codeception

Afim de realizar testes unitários no PHP utilizaremos a biblioteca Codeception, o código abaixo efetuará a instalação dos novos recursos:

composer require "codeception/codeception:*"

ou apenas

php -r '
$f="composer.json";$c="codeception";$j=json_decode(file_get_contents($f),1);
$j["require"]["$c/$c"]="*";file_put_contents($f, json_encode($j, JSON_PRETTY_PRINT)));
composer install
'

A instalação via composer tende a criar o executável no seguinte diretório ./vendor/bin/codecept, para criar a estrutura de testes é necessário executar o comando ./vendor/bin/codecept bootstrap, que exibirá resultados similares aos descritos abaixo:

john@stormtrooper101:/var/www/CodeIgniter$ ./vendor/bin/codecept bootstrap
Initializing Codeception in /var/www/CodeIgniter

File codeception.yml created       <- global configuration
tests/unit created                 <- unit tests
tests/unit.suite.yml written       <- unit tests suite configuration
tests/functional created           <- functional tests
tests/functional.suite.yml written <- functional tests suite configuration
tests/acceptance created           <- acceptance tests
tests/acceptance.suite.yml written <- acceptance tests suite configuration
tests/_output was added to .gitignore
tests/_bootstrap.php written <- global bootstrap file
Building initial Tester classes
Building Actor classes for suites: functional, acceptance, unit
\FunctionalTester includes modules: Filesystem, FunctionalHelper
FunctionalTester.php generated successfully. 13 methods added
\AcceptanceTester includes modules: PhpBrowser, AcceptanceHelper
AcceptanceTester.php generated successfully. 48 methods added
\UnitTester includes modules: Asserts, UnitHelper
UnitTester.php generated successfully. 17 methods added

Bootstrap is done. Check out /var/www/CodeIgniter/tests directory
john@stormtrooper101:/var/www/CodeIgniter$

Para criar um teste utilizando o codeception execute o comando ./vendor/bin/codecept generate:cept acceptance novoTeste, isso criará um arquivo em tests/acceptance/novoTesteCept.php dentro do qual, como exemplo, utilize o seguinte conteúdo:

<?php
$I = new AcceptanceTester($scenario);
$I->wantTo('Verificando se o CodeIgniter carrega devidamente');
$I->amOnPage('/');
$I->see('Welcome to CodeIgniter'); // Procura por texto
?>

Configure a URL da aplicação no arquivo tests/acceptance.suite.yml conforme exemplo abaixo:

class_name: AcceptanceTester
modules:
    enabled:
        - PhpBrowser
        - AcceptanceHelper
    config:
        PhpBrowser:
            url: 'http://localhost/CodeIgniter/'

Se o sistema estiver devidamente configurado a execução do comando ./vendor/bin/codecept run exibirá o seguinte resultado:

Codeception PHP Testing Framework v2.0.11
Powered by PHPUnit 4.4.5 by Sebastian Bergmann.

Functional Tests (0) ------------------------
---------------------------------------------

Acceptance Tests (1) --------------------------------------------------------------------------------------------------------
Verificando se o codeigniter carrega devidamente (novoTesteCept)                                                       Ok
-----------------------------------------------------------------------------------------------------------------------------

Unit Tests (0) ------------------------------
---------------------------------------------


Time: 123 ms, Memory: 9.50Mb

OK (1 test, 1 assertion)

Temos então o Codeception rodando nos trilhos.


Aumento de performance

É possível que as dependências do composer sejam carregadas e processadas entre 20% e 30% mais rapidamente após a utilização do comando composer dump-autoload --optimize no ambiente em que o código será executado.

Dicas de performance

Veja nos links abaixo dicas sobre como deixar seu código mais veloz, o que priorizar e o que evitar.


Resolvendo problemas

O endereço http://project.dev/ não funciona

Certifique-se de que esse nome de domínio esteja sendo devidamente direcionado à sua máquina com o comando ping project.dev -c 1, ele deve exibir o seguinte resultado:

PING project.dev (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_req=1 ttl=64 time=0.036 ms

--- project.dev ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.036/0.036/0.036/0.000 ms

Caso o resultado seja ping: unknown host project.dev ou seja diferente do exibido acima verifique seu arquivo /etc/hosts efetue novamente o passo Configurando o arquivo de Hosts.

Se o resultado do ping for similar e o site não estiver funcionando verifique se o arquivo de configuração do site está devidamente configurado executando o comando apachectl configtest, caso tudo esteja correto você deve receber um retorno similar a Syntax OK. Caso contrário, havendo algum erro, uma mensagem similar será exibida descrevendo o erro:

apache2: Syntax error on line 268 of /etc/apache2/apache2.conf: Syntax error on line 17 of /etc/apache2/sites-enabled/project: Expected </VirlHost> but saw </VirtualHost>
Action 'configtest' failed.
The Apache error log may have more information.

No caso apresentado, o problema é que a tag de abertura esperada é diferente da encontrada. Sendo, nesse caso, VirlHost a existente e VirtualHost a correta. Caso não tenha sido efetuado quaisquer modificação no arquivo de configuração do site execute novamente o passo de Configurando o Apache2.

A Database Error Occurred

Table 'project.ci_session' doesn't exist

Isso significa que houveram problemas na execução dos passos descritos em Criando e configurando o banco de dados MySQL e a tabela de sessões não foi criada. Você pode criar a tabela diretamente no console utilizando os dados abaixo:

CREATE TABLE IF NOT EXISTS `ci_sessions` (
  `id` varchar(40) NOT NULL,
  `ip_address` varchar(45) NOT NULL,
  `timestamp` int(10) unsigned DEFAULT 0 NOT NULL,
  `data` blob DEFAULT '' NOT NULL,
  PRIMARY KEY (id),
  KEY `ci_sessions_timestamp` (`timestamp`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

Desenvolvimento

CRUD

Modelagem e controle

A modelagem, bem como criação das tabelas, adição de campos e respectivos relacionamentos é automatizada pelo framework a partir da configuração da respectiva classe modelo e seu gerenciamento se dá automaticamente através do respectivo webservice.

Usuários e gerenciamento de acesso

A autenticação não é responsável por validar regras de acesso, estas serão definidas em um model específico e gerenciadas de forma independente.

Integração com template

Paleta de cores

Estamos utilizando a seguinte paleta de cores para geração dos gráficos http://paletton.com/#uid=74h0u0kllllaFw0g0qFqFg0w0aF


Geradores de documentação

Estamos testando duas ferramentas de geração de documentação. Essas ferramentas devem servir para analisar o código gerado e, a partir do mesmo, de seus comentários e de demais informações levantadas do ambiente fornecer a mais completa documentação possível com o menor conjunto de requisitos.

Utilize modelos de documentação conhecidos como DocBlocks.

Confira abaixo as ferramentas.

phpDox

O phpDox é uma ferramenta de geração de documentação relativamente simples, ele é organizado, leve e possui poucas dependências. O seu diferencial é que ele analisa as mensagens de versionamento (no nosso caso do .git) e inclui na documentação como histórico do mesmo. Ele também efetua os testes unitários utilizando o PHPUnit.

Para mais informações verifique no GitHub.

Instalação e configuração

Execute as linhas abaixo para instalar a aplicação em

wget http://phpdox.de/releases/phpdox.phar
chmod +x phpdox.phar
sudo mv phpdox.phar /usr/local/bin/phpdox
phpdox --skel > phpdox.xml

O arquivo de configuração phpdox.xml deve ser criado na raiz da aplicação. As definições abaixo efetuam documentação apenas da pasta ./application e buscam histórico nos arquivos do git.

<?xml version="1.0" encoding="utf-8" ?>
<phpdox xmlns="http://xml.phpdox.net/config" silent="false">
  <bootstrap />
  <project name="project" source="${basedir}/application" workdir="${basedir}/build/phpdox/xml">
    <collector publiconly="false" backend="parser">
      <include mask="*.php" />
      <exclude mask="" />
    </collector>

    <generator output="${basedir}/docs">
      <enrich base="${basedir}/build">
        <source type="build" />

        <source type="git">
          <git binary="/usr/bin/git" />
          <history enabled="true" limit="15" cache="${phpDox.project.workdir}/gitlog.xml" />
        </source>
      </enrich>

      <build engine="html" enabled="true" output="html">
        <template dir="${phpDox.home}/templates/html" />
        <file extension="xhtml" />
      </build>
    </generator>
  </project>
</phpdox>

Execução e visualização

Para gerar a documentação execute phpdox. A documentação estará acessível na pasta ./docs/phpdox de seu projeto.

Problemas

Caso a seguinte mensagem de erro seja exibida verifique abaixo pelas possíveis mensagens de erro e respectivas soluções:

Sorry, but your PHP environment is currently not able to run phpDox due to
the following issue(s):

ext/xsl not installed/enabled

Em sistemas baseados em Debian Linux execute o comando sudo apt-get install php5-xsl. Nos demais sistemas verifique se a extensão ext/xsl existe e foi declarada na configuração do seu php, para isso procure pela linha extension=php_xsl.dll dentro de seu arquivo php.ini.

phpLoc

PHPLoc é uma ferramenta para medir e analizar estruturas no projeto.

Não é necessário instalar ou utilizar o phpLoc. Ele foi incluído apenas com finalidade de teste. Para complementar a geração da documentação do phpDox.

instalação

Para instalação execute os comandos abaixo:

wget https://phar.phpunit.de/phploc.phar
chmod +x phploc.phar
mv phploc.phar /usr/local/bin/phploc

APIGen

APIGen é uma ferramenta de geração simples de documentação do projeto. Para mais informações visite seu repositório no GitHub.

Instalação e configuração

Para instalação execute os comandos abaixo:

wget http://apigen.org/apigen.phar
chmod +x apigen.phar
sudo mv apigen.phar /usr/local/bin/apigen
apigen generate --help

Este aplicativo não necessita de nenhuma configuração específica. Sua execução completa pode ser dada via linha de comando.

Execução e visualização

Para gerar a documentação execute apigen generate --source application --destination docs/apigen. A documentação estará disponível na pasta ./docs/apigen de seu projeto.

Dicas de formatação dos comentários

Anotações suportadas podem ser encontradas no seu repositório no GitHub.

phpDocumentor

Essa ferramenta informa os erros ou omissões no próprio terminal, sendo possível melhorar a documentação durante o processo de geração da mesma.

Instalação e configuração

Para instalação execute os comandos abaixo:

wget http://phpdoc.org/phpDocumentor.phar
chmod +x phpDocumentor.phar
sudo mv phpDocumentor.phar /usr/local/bin/phpdocumentor

Este aplicativo não necessita de nenhuma configuração específica. Sua execução completa pode ser dada via linha de comando.

Execução e visualização

Para gerar a documentação execute phpdocumentor run -d application -t docs/phpdoc. A documentação estará disponível na pasta ./docs/phpdoc de seu projeto.

Smart Comments

É uma ferramenta de auxílio na criação de comentários de arquivos JS, gerando as estruturas iniciais (se inexistentes). Veja mais no GitHub.

Instale essa ferramenta com o comando sudo npm install -g smartcomments.

A execução se dá da com o seguinte comando smartcomments -g -t assets/js/arquivo.js.

JSDocs

Gerador de documentação específico para javascript. sudo npm install -g jsdocs.

Script de documentação

O script de geração de documentação cria a documentação utilizando as três ferramentas citadas anteriormente.

#!/bin/bash

phpdocumentor run -d application -t docs/phpdoc
apigen generate --source application --destination docs/apigen
phpdox

API ProjectCRUD

Requisições / Abertura de ticket

As requisições do sistema são manipuladas diretamente pela criação de tickets, tornando assim as requisições de inserção, edição e deleção de dados mais segura e fornecendo uma linha de acompanhamento do fluxo de uso. Operações de listagem de dados não requerem ticket.

Configurando Controller/Model

O controller deve obrigatoriamente extender a classe MY_Controller, conforme exemplo abaixo:

defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Classe controller de exemplo
*
* Gerenciamento de usuários e políticas de acesso.
*
* Essa aplicação requer CodeIgniter 3.+.
*
* @package    Project extends CodeIgniter
* @subpackage project
* @author     John Murowaniecki <jmurowaniecki@gmail.com>
* @since      v2.0.1
* @category   Core/CRUD
*/
class Examples extends MY_Controller
{
    /**
    * @var string $scaffold Determina que será utilizado o model example
    */
    var $scaffold = 'example';

    /**
    * @var false $need_auth Desabilita a necessidade de login
    */
    var $need_auth = false;

    /**
    * @var string $force_format Força o formato de entrega em JSON para o scaffold
    */
    var $force_format = 'json';
}

O model deve obrigatoriamente extender a classe MY_Model, conforme exemplo abaixo:

defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Classe model de exemplo
*
* Essa aplicação requer CodeIgniter 3.+.
*
* @package    Project extends CodeIgniter
* @subpackage project
* @author     John Murowaniecki <jmurowaniecki@gmail.com>
* @since      v2.0.1
* @category   Core/CRUD
*/
class Example extends MY_Model
{
    /**
    * @var string $table Nome da tabela no banco de dados
    */
    var $table = 'example';

    /**
    * @var array $fields[] Informa a estrutura dos campos a serem utilizados do banco de dados
    */
    var $fields = array(
        'message' => array('type' => 'varchar(128)', 'null' => 'NOT NULL', 'opt' => ''),
        );

    /**
    * Executa rotinas de instalação da tabela e configuração dos campos.
    *
    * @access public
    * @return void
    */
    public function __construct()
    {
        parent::__construct();

        $this->install();
    }
}

Efetuando requisições via cURL

As requisições de teste serão feitas utilizando o cURL, entenda abaixo a sintaxe dos comandos que utilizaremos.

curl -vvv \
    -X POST \
    --data 'ticket=fcd5b7e0b09660f44a32d609def0e577&message=this is a simple test' \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/put'
  • Note que curl é o comando;
  • -vvv ativa o modo verboso, assim o cURL nos informará todos os cabeçalhos de nosso interesse;
  • --data '..' são os campos da postagem;
  • -H 'Cookie: sv2pannel=...' é o cookie da nossa sessão salva;
  • 'http://project.dev/examples/put' é nossa url previamente configurada, direcionando para o método put dentro do controller Examples;

Listagem e aquisição de dados

Requisições de dados podem ser efetuadas utilizando tanto métodos GET quanto POST, bastando informar as condições básicas na URL e, no caso do uso de POST, na estrutura dos dados enviados.

As condições mínimas de requisição de dados é basicamente informar a rota pretendida do controller previamente configurado. Como exemplo, uma requisição de listagem dos dados do controller Examples seria somente acessar http://project.dev/examples/get, que retornaria o seguinte JSON:

{
  "data": [],
  "count": 0
}

Note que nesse momento não há quaisquer dado cadastrado no nosso banco de dados, logo o conjunto de registros veio vazio. Verifique a seguir procedimentos de inserção e atualização dos dados.

Inserção e atualização de dados

Para efetuar uma insersão de dados é necessário adquirir previamente um ticket para essa finalidade, para isso efetuaremos uma requisição de adição (PUT ou POST) comum.

curl -vvv \
    -X POST \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/put'

Esse procedimento deve ser realizado para que possamos popular os dados enviados com o valor do ticket a validar a inserção. A seguir veja um exemplo da estrutura de resposta:

{
  "tickets": {
    "abf06644f47ef008bcd546bf3535a0c4": {
      "id": "new",
      "expires": 1426112896
    }
  },
  "message": "See procedures in the documentation."
}

Para inserir um registro no sistema é necessário utilizar um dos tickets válidos. Veja o comando abaixo:

curl -vvv \
    -X POST \
    --data 'ticket=abf06644f47ef008bcd546bf3535a0c4&message=this is a simple test' \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/put'

O comando anterior retornará uma mensagem de erro ou de sucesso juntamente do id do novo registro.

{
  "id": 1,
  "message": "Saved successfully."
}

Para efetuar atualização do registro inserido, utilize o id para localizar o ticket.

curl -vvv \
    -X GET \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/get'

Que retornará

{
  "data": [
  {
    "id": "1",
    "created": "2015-03-11 20:07:02",
    "updated": "0000-00-00 00:00:00",
    "message": "this is a simple test",
    "ticket": "a7e8c5efd032140960712a48064daeff"
  }
  ],
  "count": 1
}

Logo, se efetuarmos uma postagem referenciando o ticket dado, esta requisição atualizará nossos dados conforme exemplo abaixo:

curl -vvv \
    -X GET \
    -X POST \
    --data 'ticket=a7e8c5efd032140960712a48064daeff&message=message changed' \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/post'

Então, se efetuarmos uma nova requisição de listagem retornará a seguinte estrutura, note a mensagem trocada e a data de atualização:

{
  "data": [{
    "id": "1",
    "created": "2015-03-11 20:07:02",
    "updated": "2015-03-12 13:51:05",
    "message": "message changed",
    "ticket": "d75383413c0cf8fa8214f131c078421b"
  },
  ...
  ],
  "count": 1
}

Deleção

Gerenciando tickets

Caso já existam tickets válidos abertos é possível efetuar uma listagem dos mesmos através do comando:

curl -vvv \
    -X POST \
    -H 'Cookie: sv2pannel=6e7de4dc83a8348036f7f644b62148c68ec44d09;' \
    'http://project.dev/examples/tickets'

Tickets expiram por padrão em 30 segundos (esse limite poderá ser aumentado na variável $ticket_expiration no próprio controller e demais restrições podem ser inclusas). Cada procedimento de inclusão ou alteração demandam o consumo de um ticket válido.

Manipulando resultados

Um resutlado típico para

A resposta é formatada como JSON. Em caso de erro a estrutura será apenas o cabeçalho da requisição e uma mensagem no seguinte formato:

De acordo com os padrões especificados na documentação do W3 há a possibilidade de manipulação do texto de retorno da requisição, desde que o código utilizado para a mesma seja respeitado.

Efetuando requisições via Javascript

Com a biblioteca existente em api.js é possível realizar todos os procedimentos citados acima utilizando tão somente o console de seu navegador, facilitando assim a depuração e testes visto que o navegador se encarrega de lidar com os cookies.

Estrutura da interface em javascript:

var Examples = new APIController('examples'), // carrega na variável o controller juntamente dos métodos de exemplo.

  condicao = { // configura as condições necessárias para a filtragem
    like: {
      message: 'test'
    }
  },

  callback = function (d) { // cria uma função para ser utilizada como callback nos exemplos
    var i;

    for (i in d.data) {
      if (d.data.hasOwnProperty(i)) {
        console.log(d.data[i].message);
      }
    }
  };

Examples.where(condicao); // seta as condições
Examples.get(callback); // efetua a requisição

// O exemplo acima explora a forma tradicional de escrita do javascript, porém o mesmo resultado pode ser obtido
// conforme os códigos abaixo:

new APIController('examples').where(condicao).get(callback);
// É possível utilizar o controller e seus métodos sem a necessidade de instanciar em uma variável.

// Também é possível definir todas as condições para a busca e a função de tratamento dos dados recebidos sem a
// necessidade de atribuir o controller ou seus derivados a variáveis.
new APIController('examples')
  .where({ // configura as condições necessárias para a filtragem
    like: {
      message: 'test'
    }
  })
  .get(function (d) { // cria uma função para ser utilizada como callback nos exemplos
    var i;

    for (i in d.data) {
      if (d.data.hasOwnProperty(i)) {
        console.log(d.data[i].message);
      }
    }
  });
});

Aplicação de tema Bootstrap

A aplicação dos temas é variável, utilize a documentação disponibilizada em getbootstrap. No código abaixo será criada uma estrutura padrão inicial contendo os recursos minificados do bootstrap:

wget https://github.com/twbs/bootstrap/releases/download/v3.3.2/bootstrap-3.3.2-dist.zip
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment