HUFERSIL.WEBDEVELOPER

Criando um catálogo de produtos com Lumine e CodeIgniter – Parte 5

Olá pessoal!

Hoje, vamos deixar nossa página um pouco mais apresentável e também fazer o cadastro de categorias.

Neste sistema, as categorias podem ter vários níveis. Então, nosso sistema deverá contemplar o cadastro de categorias filhas, netas, bisnetas…

Deixando a interface mais bonita

Antes de continuar a desenvolver as funcionalidades, vamos deixar o visual mais bacana.

Abra o arquivo application/views/admin/template.php e adicione depois da tag body o código abaixo:

 
<!-- imediatamente após o <body> -->
<div class="header">
	<?php if(!empty($_usuario)): ?>
	<ul>
		<li><a href="<?php echo site_url('admin/dashboard/index'); ?>">Página Inicial</a>
		<li><a href="<?php echo site_url('admin/categorias/index'); ?>">Categorias</a>
		<li><a href="<?php echo site_url('admin/produtos/index'); ?>">Produtos</a>
		<li><a href="<?php echo site_url('admin/usuarios/index'); ?>">Usuários</a>
		<li><a href="<?php echo site_url('admin/login/do_logout'); ?>">Sair</a>
	</ul>
	<?php endif; ?>
</div>

Este será o cabeçalho de nossa aplicação. Ele também já tem um menu básico sobre as coisas que vamos desenvolver.

Agora criando um footer simples. Adicione antes da tag de fechamento "body":

<div class="footer">
	HUFERSIL.WEBDEVELOPER - <?php echo date('Y'); ?>
</div>

Após adicionar estes códigos, precisamos estiliza-los. Para isso, abra o arquivo assets/css/admin.css e adicione os estilos abaixo:

.header {
	width: 85%;
	margin-left: auto;
	margin-right: auto;
	margin-bottom: 20px;
	padding: 80px 10px 10px 10px;
	background-image: URL(../imagens/logo.gif);
	background-position: left top;
	background-repeat: no-repeat;
	border-bottom: 2px solid #FFCC00;
}

.header ul {
	padding: 0px;
	margin: 0px;
}

.header ul li {
	list-style: none;
	display: inline;
	margin-right: 20px;
}
.header ul li a {
	text-decoration: none;
	color: #000000;
}
.header ul li a:hover {
	font-weight: bold;
	color: #0000CC;
}

.footer {
	width: 85%;
	margin-left: auto;
	margin-right: auto;
	margin-top: 20px;
	padding: 10px;
	text-align: center;
	font-size: 9px;
	color: #CCCCCC;
}

Pronto agora, temos um visual mais bacana para podermos acessar os itens que criamos.

Criando a controller de gerenciamento de categorias

Agora, precisamos cadastrar as categorias, antes de podermos cadastrar produtos. Para esta parte de categorias, como vamos poder trabalhar com estrutura hierarquica, precisaremos de algumas funções extras para podermos exibir os dados de forma hierarquica/aninhada.

Este tipo de função, na estrutura do CodeIgniter, fica dentro de arquivos chamados helpers (http://codeigniter.com/user_guide/general/helpers.html). Vamos criar um arquivo de helper, no caminho application/helpers/util_helper.php, com a seguinte função:

/**
 * Recebe uma lista de elementos e os coloca de forma aninhada.
 *
 * @param array $lista       Lista de elementos a ser aninhada
 * @param string $chave      Indice que contem o valor padrao (codcategoria, por exemplo)
 * @param string $chavePai   Indice que contem o valor do codigo pai (codpai, por exemplo)
 * @param string $propFilhos Novo indice a ser gerado que armazenara os elementos aninhados
 * @return array Lista com os elementos aninhados
 * @author Hugo Ferreira da Silva
 */
function fncAninharElementos(array $lista, $chave, $chavePai, $propFilhos = '_filhos'){

	// 1 - colocando os itens indexados pelo codigo principal
	$tmp = array();
	foreach($lista as $item){
		$tmp[$item[$chave]] = $item;
	}

	// 2 - colocando um item dentro do outro,
	// conforme pais e filhos
	foreach($tmp as &$item){
		if(!empty($item[$chavePai])){
			$tmp[$item[$chavePai]][$propFilhos][] = &$item;
		}
	}

	// 3 - deixando somente os itens que nao tem pai
	$nova = array();
	foreach($tmp as $node){
		if(empty($node[$chavePai])){
			$nova[] = $node;
		}
	}

	// pronto, retornando os elementos
	return $nova;
}

Depois de criar o arquivo, temos que carregar ele. Poderiamos utilizar o autoload, mas neste exemplo, vamos fazer um pouco diferente. Abra o arquivo application/core/MY_Controller.php e adicione ao construtor as seguintes linhas:

// carregando a helper customizada
$this->load->helper('util');

Pronto. Agora podermos utilizar as funções que estão dentro de arquivo. Agora, vamos criar nossa controller de gerenciamento de caregorias.

Crie um arquivo na estrutura application/controllers/admin/categorias.php com o seguinte conteúdo:

<?php
/**
 * Controller para gerenciamento dos categorias de produtos
 *
 *
 * @link http://www.hufersil.com.br
 * @author Hugo Ferreira da Silva
 *
 */
class Categorias extends AdminController {

	/**
	 * Metodo para listar as categorias cadastradas
	 *
	 * @author Hugo Ferreira da Silva
	 */
	public function index(){
		$listaAninhada = $this->getCategoriasAninhadas();

		// colocamos a lista de resultados
		$this->assign('lista', $listaAninhada);
		// exibimos o resultado
		$this->display('categorias/index');
	}

	/**
	 * Exibe a tela de inserção/atualização de categorias
	 *
	 * @param int $id Codigo da categoria que esta sendo editada ou null para cadastrar uma nova
	 * @author Hugo Ferreira da Silva
	 */
	public function editar($id = null){
		// se tem codigo de usuario e o metodo é GET
		if(!empty($id) && $_SERVER['REQUEST_METHOD'] == 'GET'){
			// resgatamos o registro do banco
			$obj = new Categoria();
			$obj->get($id);
			// colocamos os dados na variavel $_POST
			$_POST = $obj->toArray();
		}
		// colocamos a lista de categorias para exibirmos na combo
		$this->assign('categorias', $this->getCategoriasAninhadas());

		// exibimos a tela de inserção/edição de categorias
		$this->display('categorias/editar');
	}

	/**
	 * Insere uma nova categoria ou altera os dados de uma existente
	 *
	 * @param int $id Código da categoria ou null para uma nova
	 * @author Hugo Ferreira da Silva
	 */
	public function salvar($id = null){
		// cria um objeto para persistencia
		$dao = new Categoria();

		// se está editando (informou o código da categoria)
		if(!empty($id)){
			// pegamos o registro do banco de dados
			$dao->get($id);
		}

		// se não tiver código pai, colocamos como NULL
		if(empty($_POST['codpai'])){
			$_POST['codpai'] = null;
		}

		// colocamos no objeto os valores que estão no formulario
		// como os nomes dos campos tem os mesmos nomes dos campos,
		// este método facilita o preenchimento do objeto
		$dao->populateFrom($_POST);

		// fazemos a validação dos dados
		$erros = $dao->validate();

		// se não houveram erros
		if(empty($erros)){
			// fazemos a persistencia
			$dao->save();

			// redirecionamos para a tela de listagem de categorias
			redirect('admin/categorias/index');

		// mas se não passou na validação
		} else {
			// informamos quais foram os erros
			$this->assign('erros', $erros);

			// exibimos a tela de inserção/edição
			$this->editar($id);
		}
	}

	/**
	 * Remove uma categoria cadastrada
	 *
	 * @param int $id Código da categoria a ser removida
	 * @author Hugo Ferreira da Silva
	 */
	public function remover($id){
		// criamos um objeto de categoria
		$user = new Categoria();

		// se informou o codigo da categoria e encontrou no banco
		if(!empty($id) && $user->get($id) == 1){
			// removemos a categoria
			// as subcategorias serão removidas em cascata,
			// bem como os produtos
			$user->delete();
		}

		// redirecionamos para a tela de listagem de categorias
		redirect('admin/categorias/index');
	}

	/**
	 * Recupera as categorias de forma aninhada
	 *
	 * @author Hugo Ferreira da Silva
	 */
	protected function getCategoriasAninhadas(){
		$lista = new Categoria();
		// fazemos a consulta
		$lista->alias('c')
			// ordenamos pelo nome
			->order('c.ordem asc')
			// efetuamos a consulta
			->find();

		// precisamos colocar um "item dentro do outro" para poder enviar
		// e montar o html corretamente
		$listaAninhada = fncAninharElementos($lista->allToArray()
			, 'codcategoria'
			, 'codpai'
			, 'categorias'
		);

		return $listaAninhada;
	}
}

Nesta parte de categorias, precisamos fazer uma verificação para saber se o usuário não está inserindo referências circulares (um objeto é pai/avô dele mesmo).

Para adicionarmos esta validação, abra o arquivo application/models/Categoria.php e adicione os seguintes métodos:

    public function validate(){
    	$this->addValidator(new Lumine_Validator_String('nome', 'Informe o nome da categoria'));
    	$this->addValidator(new Lumine_Validator_Custom('codpai', 'Referência ciclica detectada', array($this,'validarCodPai')));
    	return parent::validate();
    }

    /**
     * Efetua a validação contra referências circulares
     *
     * @param Categoria $obj Referencia ao objeto que está sendo validado
     * @param string $field  Nome do campo que está sendo validado
     * @param mixed $value   Valor do campo que está sendo validado
     * @return boolean
     * @author Hugo Ferreira da Silva
     */
    public function validarCodPai(Categoria $obj, $field, $value){
    	// se estiver inserindo, tudo bem
    	if(empty($obj->codcategoria)){
    		return true;
    	}

    	// se tiver vazio, virou uma categoria principal
    	if(empty($value)){
    		return true;
    	}

    	// ele não pode ser pai dele mesmo
    	if($value == $obj->codcategoria){
    		return false;
    	}

    	// e nao pode ser avo, bisavo ... dele mesmo
    	$stack = array($obj->codpai);
    	$tmp = new Categoria();

    	while(!empty($stack)){
    		$tmp->reset();
    		$tmp->select('codcategoria,codpai')
    			->where('{codcategoria} IN (?)', $stack)
    			->find();

    		$stack = array();
    		foreach($tmp->allToArray(false,true) as $item){
    			$stack[] = $item['codpai'];
    		}

    		if(in_array($obj->codcategoria, $stack)){
    			return false;
    		}
    	}

    	return true;
    }

E, para completarmos esta parte, vamos criar as views. Primeiro, a de listagem de categorias (application/views/admin/categorias/index.php):

<fieldset class="admin-container lista-resultados">
	<legend>Categorias cadastradas</legend>
	<?php if(!empty($lista)): ?>
		<table class="tabela-resultados">
			<thead>
				<tr>
					<th>Código</th>
					<th>Nome</th>
					<th>Ações</th>
				</tr>
			</thead>
			<?php 

			// funcao para criar uma linha na tabela de listagem
			// ela eh chamada recursivamente para mostrar os elementos aninhados
			function createRow($stack, $indent = 0){
				// para cada item na pilha
				while(!empty($stack)){
					$item = array_shift($stack);

					// criamos uma linha na tabela
					printf('<tr>
							<td width="5%%">%d</td>
							<td width="85%%" style="padding-left: %dpx">%s</td>
							<td width="10%%">
								<a href="%s" title="Editar" class="btnEditar">Editar</a>
								<a href="%s" title="Remover" class="btnRemover">Remover</a>
							</td>
						</tr>'
						, $item['codcategoria']
						, $indent * 30
						, $item['nome']
						, site_url('admin/categorias/editar/'.$item['codcategoria'])
						, site_url('admin/categorias/remover/'.$item['codcategoria'])
					);

					if(!empty($item['categorias'])){
						createRow($item['categorias'], $indent+1);
					}
				}
			}

			createRow($lista);
			?>
		</table>
	<?php else: ?>
		<p>Nenhum registro encontrado</p>
	<?php endif; ?>
</fieldset>

E agora, a de inserção e edição (application/views/admin/categorias/editar.php):

<fieldset class="admin-container form-padrao">
	<legend>Cadastro de Categorias - <?php echo $this->uri->segment(4) == '' ? 'Adicionar' : 'Atualizar'; ?></legend>
	<?php
	// se houveram erros
	if(!empty($erros)){
		// indicamos os erros
		echo '<div class="erros">', implode("<br />", $erros), '</div>';
	}
	?>
	<form action="<?php echo site_url('admin/categorias/salvar/'.$this->uri->segment(4)); ?>" method="post">
		<p>
			<label for="codpai">Categoria Pai: </label>
			<select id="codpai" name="codpai">
				<option value="">-- Esta será uma categoria pai --</option>
				<?php 

				// funcao para criar uma linha na combo
				function createOption($stack, $indent = 0){
					$codpai = empty($_POST['codpai']) ? 0 : $_POST['codpai'];
					// para cada item na pilha
					while(!empty($stack)){
						$item = array_shift($stack);

						// criamos uma linha na tabela
						printf('<option value="%d" %s>%s</option>'
							, $item['codcategoria']
							, $codpai == $item['codcategoria'] ? ' selected="selected"' : ''
							, str_repeat(' ', $indent * 5) . $item['nome']
						);

						if(!empty($item['categorias'])){
							createOption($item['categorias'], $indent+1);
						}
					}
				}

				createOption($categorias);

				?>
			</select>
		</p>
		<p>
			<label for="nome">Nome: </label>
			<input type="text" name="nome" id="nome" value="<?php echo $this->input->post('nome'); ?>" />
		</p>
		<p>
			<label for="ordem">Ordem: </label>
			<input type="text" name="ordem" id="ordem" value="<?php echo $this->input->post('ordem'); ?>" />
		</p>
		<p>
			<input type="submit" name="btnSalvar" id="btnSalvar" value="Salvar" />
			<input type="button" name="btnCancelar" id="btnCancelar" value="Cancelar" onclick="location.href='<?php echo site_url('admin/categorias/index');?>'" />
		</p>
	</form>
</fieldset>

É isso. Efetue seu login e teste a aplicaçao!

Abraços e fiquem com Deus!

Deixar uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *

*

Pode usar estas etiquetas HTML e atributos: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">