HUFERSIL.WEBDEVELOPER

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

Hoje vamos para a sexta parte de nossa jornada.

Esta é uma das mais complexas, pois vamos cadastrar os produtos, o que envolve tratamento das imagens e vinculo com as categorias.

Criando um método auxiliar

Antes de iniciarmos, abra o arquivo application/helpers/util_helper.php e adicione a seguinte função:

/**
 * Função para gerar um HTML a partir de um modelo de uma lista aninhada
 *
 * @param array $lista Lista de elementos aninhados
 * @param string $tpl Modelo para aplicar as substituições
 * @param string $nomeIndice Nome do indice do array que contém elementos filhos
 * @param string $indentString String usada para indentação
 * @param int $level Nivel atual do laço
 * @return Ambigous <string, mixed>
 * @author Hugo Ferreira da Silva
 */
function fncImprimeConteudoAninhado(array $lista, $tpl, $nomeIndice, $indentString = ' ', $level = 0){
	$str = '';
	foreach($lista as $item){
		$item['_indent'] = str_repeat($indentString, $level);
		$str .= preg_replace('@:(\w+)\b@e', '$item["$1"]', $tpl);

		if(!empty($item[$nomeIndice])){
			$str .= fncImprimeConteudoAninhado($item[$nomeIndice], $tpl, $nomeIndice, $indentString, $level+1);
		}
	}

	return $str;
}

Este método nos auxiliará para criarmos tabelas ou outros elementos que dependam de um loop de uma lista, e, inclusive, de modo recursivo.

Para esta parte de produtos, vamos precisar também da biblioteca JavaScript jQuery. Vamos usá-la na parte de ordenação das fotos dos produtos.

Abra o arquivo application/views/admin/template.php e adicione na seção head as linhas abaixo:

	<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
	<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.17/jquery-ui.min.js" type="text/javascript"></script>
	<script src="http://jquery-ui.googlecode.com/svn/tags/latest/external/jquery.bgiframe-2.1.2.js" type="text/javascript"></script>
	<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.17/themes/base/jquery-ui.css" type="text/css" media="all" />

Criando a controller de produtos

Crie um arquivo na seguinte estrutura: application/controllers/admin/produtos.php e cole o conteúdo abaixo:

<?php
/**
 * Controller para gerenciar o cadastro de produtos
 *
 *
 * @author Hugo Ferreira da Silva
 *
 */
class Produtos extends AdminController {

	/**
	 * Lista os produtos cadastrados
	 *
	 * @param int $offset Registro inicial
	 * @author Hugo Ferreira da Silva
	 */
	public function index($offset = 0){
		$lista = new Produto();
		$lista->alias('p')
			->join(new Categoria(),'inner','c')
			->join(new Foto(),'left','f',null,null,'f.ordem = 1')
			->selectAs()
			->select('c.nome as categoria, f.thumb as foto');

		if($this->input->post('nome') != '') {
			$lista->where('p.nome like ?', $this->input->post('nome'));
		}
		if($this->input->post('descricao') != ''){
			$lista->where('p.descricao like ?', $this->input->post('descricao'));
		}
		if($this->input->post('codcategoria') != ''){
			$lista->where('p.codcategoria = ?', $this->input->post('codcategoria'));
		}

		$total = $lista->count();

		$lista->order('p.nome asc')
			->limit($offset, LIMITE_PAGINACAO_ADMIN)
			->find();

		$categoria = new Categoria();
		$categoria->alias('c')
			->order('c.ordem asc')
			->find();

		$listaAninhada = fncAninharElementos(
		$categoria->allToArray()
			, 'codcategoria'
			, 'codpai'
			, 'categorias'
		);

		$this->assign('categorias', $listaAninhada);
		$this->assign('lista', $lista->allToArray());
		$this->assign('total', $total);

		$this->display('produtos/index');
	}

	/**
	 * Exibe a tela de inserção/alteração de produtos
	 *
	 * @param int $id Código do produto que está sendo editado ou null para um novo produto
	 * @author Hugo Ferreira da Silva
	 */
	public function editar($id = null){
		if(!empty($id) && $_SERVER['REQUEST_METHOD'] == 'GET'){
			$obj = new Produto();
			$obj->get($id);
			$_POST = $obj->toArray();

			$foto = new Foto();
			$foto->codproduto = $id;
			$foto->order('ordem')
				->find();

			$_POST['fotos'] = $foto->allToArray();
		}

		$categoria = new Categoria();
		$categoria->alias('c')
			->order('c.ordem asc')
			->find();

		$listaAninhada = fncAninharElementos(
			$categoria->allToArray()
			, 'codcategoria'
			, 'codpai'
			, 'categorias'
		);

		$this->assign('categorias', $listaAninhada);
		$this->display('produtos/editar');
	}

	/**
	 * Salva as alterações de um produto ou insere um novo
	 *
	 * @param int $id Código do produto sendo alterado ou null para inserir um novo
	 * @author Hugo Ferreira da Silva
	 */
	public function salvar($id = null){
		$obj = new Produto();
		if(empty($id)){
			$_POST['dataCadastro'] = time();
		} else {
			$obj->get($id);
		}

		// setando os valores que vieram do formulario
		$obj->populateFrom($_POST);
		// remove o indice de fotos (trataremos mais na frente)
		$obj->setFieldValue('fotos', array());
		// valida
		$erros = $obj->validate();
		// se passou
		if(empty($erros)){

			$obj->save();

			// salvando as fotos antigas
			$codFotos = array(0);
			if(!empty($_POST['fotos'])){
				foreach($_POST['fotos'] as $item){
					$foto = new Foto();
					$foto->populateFrom($item);
					$foto->save();
					$codFotos[] = $foto->codfoto;
					$foto->destroy();
				}
			}

			// extensoes permitidas
			$extensoes = array('jpeg','jpg','gif','png');
			$pasta     = 'assets/imagens/produtos/';

			// salvando as fotos novas
			if(!empty($_FILES['novas_fotos']['tmp_name'])){
				$count = count($_FILES['novas_fotos']['tmp_name']);
				for($i=0; $i<$count; $i++){
					$tmp  = $_FILES['novas_fotos']['tmp_name'][$i];
					$name = $_FILES['novas_fotos']['name'][$i];

					// se tem arquivo eh tah nas extensoes permitidas
					if(!empty($tmp) && preg_match('@\.('.implode('|',$extensoes).')$@i', $name)){
						$filename = sprintf('%05d-%05d_%05d_%05d.jpg', $obj->codproduto, rand(0,time()), rand(0,time()), rand(0,time()));
						$foto             = new Foto();
						$foto->codproduto = $obj->codproduto;
						$foto->ordem      = $foto->count() + 1;
						$foto->grande     = $pasta . $filename;
						$foto->thumb      = $pasta . 'thumb_'.$filename;
						$foto->save();

						// gerando o thumb
						$this->gerarImagens($tmp, $pasta . $filename, $foto->thumb);

						$codFotos[] = $foto->codfoto;
					}
				}
			}

			// removendo as fotos que não foram salvas
			$foto = new Foto();
			$foto->alias('f')
				->where('f.codfoto NOT IN(?) AND f.codproduto = ?',$codFotos, $obj->codproduto)
				->find();

			while($foto->fetch()){
				unlink($foto->thumb);
				unlink($foto->grande);
			}

			$foto->reset();
			$foto->where('{codfoto} NOT IN(?) AND {codproduto} = ?',$codFotos, $obj->codproduto)
				->delete(true);

			redirect('admin/produtos/index');
		} 

		$this->assign('erros', $erros);
		$this->editar($id);
	}

	/**
	 * Remove um produto
	 *
	 * @param int $id Código do produto a ser removido
	 * @author Hugo Ferreira da Silva
	 */
	public function remover($id){
		// primeiro, removemos as fotos
		$foto = new Foto();
		$foto->where('{codproduto} = ?',$id)
			->find();

		while($foto->fetch()){
			unlink($foto->grande);
			unlink($foto->thumb);
		}

		// removendo o produto
		$produto = new Produto();
		if($produto->get($id) == 1){
			$produto->delete();
		}

		redirect('admin/produtos/index');
	}

	/**
	 * Gera as imagens pequenas e grandes para um produto
	 *
	 * @param string $old Caminho da imagem original
	 * @param string $new Caminho para a nova imagem grande
	 * @param string $thumbFilename Caminho para o thumb a ser gerado
	 * @author Hugo Ferreira da Silva
	 */
	protected function gerarImagens($old, $new, $thumbFilename){
		$dir     = dirname($new);

		if(!is_dir($dir)){
			mkdir($dir, 0777, true);
		}

		// gerando o thumb
		$w = 100;
		$h = 100;
		$nh = 0;
		$nw = 0;
		$zoom = 1;
		list($width,$height) = getimagesize($old);
		if($width>$height){
			$zoom = $w/$width;
			$nw   = $w;
			$nh   = $zoom * $height;
		} else {
			$zoom = $h/$height;
			$nh   = $h;
			$nw   = $zoom * $width;
		}

		$inix = round(($w - $nw)/2);
		$iniy = round(($h - $nh)/2);
		$thumb = imagecreatetruecolor($w, $h);
		$white = imagecolorallocate($thumb, 255,255,255);
		$src = imagecreatefromstring(file_get_contents($old));
		imagefilledrectangle($thumb, 0,0,$width,$height,$white);
		imagecopyresampled($thumb, $src, $inix, $iniy, 0, 0, $nw, $nh, $width, $height);
		imagejpeg($thumb, $thumbFilename, 90);

		imagedestroy($thumb);

		// fazendo o resize da imagem grande
		$maxheight = 500;
		$maxwidth = 800;
		if($width > $maxwidth || $height > $maxheight){

			if($width>$height){
				$zoom = $maxwidth/$width;
				$nw   = $maxwidth;
				$nh   = $zoom * $height;
			} else {
				$zoom = $maxheight/$height;
				$nh   = $maxheight;
				$nw   = $zoom * $width;
			}

			$img = imagecreatetruecolor($nw, $nh);
			imagecopyresampled($img, $src, 0, 0, 0, 0, $nw, $nh, $width, $height);
			imagejpeg($img, $new, 90);
			imagedestroy($img);
		} else {
			move_uploaded_file($old, $new);
		}

		imagedestroy($src);
	}
}

Como podemos ver, aqui criamos o produto e já podemos enviar também quantas imagens desejarmos para um mesmo produto.

Na lista de produtos dentro do admin, a primeira foto (a que estiver com a ordem = 1) será exibida na listagem.

Também precisamos criar as validações do produto. Para isso, abra o arquivo application/models/Produto.php e adicione o metodo a seguir, logo abaixo da linha END AUTOCODE

    public function validate(){
    	$this->addValidator(new Lumine_Validator_String('nome', 'Informe o nome'));
    	$this->addValidator(new Lumine_Validator_Number('codcategoria', 'Informe uma categoria'));
    	$this->addValidator(new Lumine_Validator_String('descricao', 'Informe a descrição'));
    	$this->addValidator(new Lumine_Validator_String('permalink', 'Informe o permalink'));
    	$this->addValidator(new Lumine_Validator_Unique('permalink', 'O permalink informa está em uso'));
    	$this->addValidator(new Lumine_Validator_Number('valor', 'Informe o valor em formato numérico'));
    	return parent::validate();
    }

Agora, vamos criar nossas views.

Primeiro a de listagem de produtos (application/views/admin/produtos/index.php):

<fieldset class="admin-container form-padrao">
	<legend>Pesquisar Produtos</legend>
	<form action="<?php echo site_url('admin/produtos/index'); ?>" method="post">
		<p>
			<label for="nome">Nome do produto</label>
			<input type="text" value="" id="nome" name="nome" />
		</p>
		<p>
			<label for="descricao">Descrição</label>
			<input type="text" value="" id="descricao" name="descricao" />
		</p>
		<p>
			<label for="codcategoria">Categoria do Produto: </label>
			<select name="codcategoria" id="codcategoria">
				<option value=""> --- SELECIONE --- </option>
				<?php
				$res = fncImprimeConteudoAninhado(
					$categorias
					, '<option value=":codcategoria">:_indent:nome</option>'.PHP_EOL
					, 'categorias'
					, '     '
				);
				if(!empty($_POST['codcategoria'])){
					$res = str_replace('value="'.$_POST['codcategoria'].'"','value="'.$_POST['codcategoria'].'" selected="selected"',$res);
				}
				echo $res;
				?>
			</select>
		</p>
		<p>
			<input type="submit" name="btnBusca" id="btnBusca" value="Consultar" />
			<input type="button" name="btnAdicionar" id="btnAdicionar" value="Adicionar Produto" onclick="location.href='<?php echo site_url('admin/produtos/editar');?>'" />
		</p>
	</form>
</fieldset>

<fieldset class="admin-container lista-resultados">
	<legend>Lista de Produtos Encontrados</legend>
	<table class="tabela-resultados">
		<thead>
			<th>Código</th>
			<th>Nome</th>
			<th>Categoria</th>
			<th>Imagem</th>
			<th>Ações</th>
		</thead>

		<tbody>
			<?php
			if(!empty($lista)){
				foreach($lista as $item){
					printf('<tr>
							<th>%05d</th>
							<th>%s</th>
							<th>%s</th>
							<th><img src="%s" alt="%s" /></th>
							<th>
								<a href="%s" class="btnEditar">Editar</a>
								<a href="%s" class="btnRemover">Remover</a>
							</th>
						</tr>
						',

						$item['codproduto'],
						$item['nome'],
						$item['categoria'],
						empty($item['foto']) ? site_url('assets/imagens/produto-padrao-thumb.png') : site_url($item['foto']),
						$item['nome'],
						site_url('admin/produtos/editar/'.$item['codproduto']),
						site_url('admin/produtos/remover/'.$item['codproduto'])
					);
				}

				// configurações da paginacao
				// @link http://codeigniter.com/user_guide/libraries/pagination.html
				$config['uri_segment'] = 4;
				$config['total_rows'] = $total;
				$config['per_page'] = LIMITE_PAGINACAO_ADMIN;
				$config['base_url'] = site_url('admin/produtos/index');
				$config['use_page_numbers'] = TRUE;
				$this->pagination->initialize($config);

				// exibe a paginacao
				printf('<tr>
							<td colspan="5"> %s </td>
						</tr>'
				, $this->pagination->create_links()
				);
			} else {
				echo '<tr><td colspan="5">Nenhum resultado encontrado</td></tr>';
			}
			?>
		</tbody>
	</table>
</fieldset>

E agora, a de cadastro e edição de produtos

<script type="text/javascript">
var tpl, tplParent;
$(function(){
	$('#container-fotos').sortable();
	$('.foto-item a').click(function(e){
		$(this).parent().remove();
		e.preventDefault();
		return false;
	});

	$('form').submit(function(){
		var i = 1;
		$('.ordem').each(function(){
			this.value = i++;
		});
		return true;
	});

	tpl = $('.enviarFoto');
	tplParent = tpl.parent();
	tpl.remove();
	addCampoFoto();
});

function addCampoFoto(){
	var item = tpl.clone().appendTo(tplParent);
	item.find('input').change(function(e){
		addCampoFoto();
	});
	item.find('a').click(function(e){
		item.closest('p').remove();
		e.preventDefault();
		return false;
	});
}

</script>

<fieldset class="admin-container form-padrao">
	<legend>Cadastro de Produtos - <?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/produtos/salvar/'.$this->uri->segment(4)); ?>" method="post" enctype="multipart/form-data">
		<!--  dados do produto -->
		<div style="float: left;">
			<p>
				<label for="nome">Nome: </label>
				<input type="text" name="nome" id="nome" value="<?php echo $this->input->post('nome'); ?>" />
			</p>
			<p>
				<label for="codcategoria">Categoria do Produto: </label>
				<select name="codcategoria" id="codcategoria">
					<option value=""> --- SELECIONE --- </option>
					<?php
					$res = fncImprimeConteudoAninhado(
						$categorias
						, '<option value=":codcategoria">:_indent:nome</option>'.PHP_EOL
						, 'categorias'
						, '     '
					);
					if(!empty($_POST['codcategoria'])){
						$res = str_replace('value="'.$_POST['codcategoria'].'"','value="'.$_POST['codcategoria'].'" selected="selected"',$res);
					}
					echo $res;
					?>
				</select>
			</p>
			<p>
				<label for="permalink">Permalink: </label>
				<input type="text" name="permalink" id="permalink" value="<?php echo $this->input->post('permalink'); ?>" />
			</p>
			<p>
				<label for="descricao">Descrição: </label>
				<textarea name="descricao" id="descricao" cols="40" rows="10" ><?php echo $this->input->post('descricao'); ?></textarea>
			</p>
			<p>
				<label for="observacoesTecnicas">Observações Técnicas: </label>
				<textarea name="observacoesTecnicas" id="observacoesTecnicas" cols="40" rows="10" ><?php echo $this->input->post('observacoesTecnicas'); ?></textarea>
			</p>
			<p>
				<label for="valor">Valor: </label>
				<input type="text" name="valor" id="valor" value="<?php echo $this->input->post('valor'); ?>" />
			</p>
		</div>

		<!-- fotos do produto -->
		<div style="float: right; width: 40%">
			<p><strong>Fotos</strong></p>
			<div id="container-fotos">
				<?php
				// se ja tiver fotos
				if(!empty($_POST['fotos'])){
					$modelo = '<div class="foto-item">
						<span>
							<img src="'.site_url(':thumb').'" />
						</span>
						<a href="#" title="Remover" class="btnRemover">Remover</a>
						<input type="hidden" name="fotos[:codfoto][codfoto]" value=":codfoto" />
						<input type="hidden" name="fotos[:codfoto][thumb]" value=":thumb" />
						<input type="hidden" name="fotos[:codfoto][grande]" value=":grande" />
						<input type="hidden" name="fotos[:codfoto][ordem]" value=":ordem" class="ordem" />
						<input type="hidden" name="fotos[:codfoto][codproduto]" value=":codproduto" />
					</div>';
					echo fncImprimeConteudoAninhado($_POST['fotos'], $modelo, 'fotos');
				}
				?>
			</div>
			<p class="enviarFoto">
				Enviar foto
				<input class="enviar-foto" type="file" name="novas_fotos[]" />
				<a href="#">Remover Foto</a>
			</p>
		</div>
		<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/produtos/index');?>'" />
		</p>
	</form>
</fieldset>

E, para finalizar, adicionamos alguns estilos para poder funcionar o drag-and-drop da parte de cadastro de imagens (no arquivo assets/css/admin.css:

strong {
	font-weight: bold;
}

.foto-item {
	width: 100px;
	height: 100px;
	float: left;
	margin-right: 10px;
	margin-bottom: 10px;
	border: 1px solid #CCCCCC;
	position: relative;
	cursor: move;
}

.foto-item span {
	display: block;
	width: 100px;
	height: 100px;
	overflow: hidden;
	position: absolute;
}

.foto-item a {
	position: absolute !important;
	right: 4px;
	bottom: 4px;
}

Suas telas deverão ficar semelhantes as abaixo:

@braç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="">