CRUD Básico com CodeIgniter 1.7.2

For those who do not speak Portuguese, you can see a working example here and download the source code here. The comments are in English.

Fiz algumas melhorias no código do post anterior:

  • A versão do CI foi atualizada. Este exemplo agora usa o CodeIgniter 1.7.2.
  • As configurações de layout da paginação foram transferidas para o lugar correto (config/pagination.php).
  • O uso da função set_value do Form helper, como sugerido no manual do CI, ajudou a enxugar o código do Controller.
  • Pequenos ajustes no método _save do Controller.
  • As pastas i, js e css foram movidas para dentro da nova pasta web, organizando os arquivos e eliminando a necessidade de manutenção nas excessões do .htaccess.
  • O profiler foi habilitado para exibir informações de performance da aplicação.

Antes de continuar, veja o exemplo funcionando aqui.

Disponibilizei o código fonte completo, junto com o codeigniter 1.7.2 e o script de criação do banco aqui.

Decidi deixar o conteúdo e os comentários em inglês para alcançar mais pessoas.

Passo 1 – Criação do banco de dados

Execute o script abaixo (utilizando o phpMyAdmin, por exemplo) para criar o banco “ci_sandbox“, criar e popular a tabela “users” no MySQL 5. Note que estou utilizando codificação UTF-8, que vem configurada por padrão no CodeIgniter (no arquivo application/config/database.php).

CREATE DATABASE `ci_sandbox` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `name`, `email`) VALUES
(1, 'Bob', 'bob@bob.com'),
(2, 'Philip', 'philip@philip.com'),
(3, 'Paty', 'paty@paty.com'),
(4, 'Kelly', 'kelly@kelly.com'),
(5, 'Bill', 'bill@bill.com'),
(6, 'Tedd', 'tedd@tedd.com'),
(7, 'John', 'john@john.com'),
(8, 'Paul', 'paul@paul.com'),
(9, 'Anna', 'anna@anna.com'),
(10, 'Hellen', 'hellen@hellen.com'),
(11, 'Kate', 'kate@kate.com');

Passo 2 – Criação do CSS da aplicação

Veja abaixo a folha de estilo usada na listagem, paginação e mensagens (arquivo web/css/style.css):

*{
	margin: 0;
	padding: 0;
}

body {
	background:#FFFFFF;
	font-family:verdana,arial,helvetica,sans-serif;
	font-size:10px;
	margin:2px;
	padding:2px;
}

#container h1 {
	color:#678197;
	border: 2px solid #E5EFF8;
	margin-bottom: 10px;
	padding: 5px;
	width: 686px;
	_width: 700px;
	background-color: #F2F6F7;
	font-size: 20px;
}

#listing {
	width:700px;
	color:#678197;
	font-family:verdana,arial,helvetica,sans-serif;
	font-size:8pt;
	border-spacing: 0px;
	border: 1px solid #E5EFF8;
}

#listing td {
	padding:5px;
	font-size:8pt;
	border: 1px solid #E5EFF8;
}
#listing th {
	color:#21497D;
	font-weight:bold;
	background:#F2F6F7;
	font-size:8pt;
	padding:5px;
	text-align: left;
	border: 1px solid #E5EFF8;
}

ul {
	border:0; margin:0; padding:0;
}

#pagination li {
	border:0; margin:0; padding:0;
	font-size:11px;
	list-style:none;
	float:left;
}

#pagination a {
	border:solid 1px #DDDDDD;
	margin-right:2px;
}

#pagination .previous-off, #pagination .next-off {
	color:#666666;
	display:block;
	float:left;
	font-weight:bold;
	padding:3px 4px;
}

#pagination .next a, #pagination .previous a {
	font-weight:bold;
	border:solid 1px #FFFFFF;
}

#pagination .active {
	color:#ff0084;
	font-weight:bold;
	display:block;
	float:left;
	padding:4px 6px;
}

#pagination a:link, #pagination a:visited {
	color:#678197;
	display:block;
	float:left;
	padding:3px 6px;
	text-decoration:none;
}

#pagination a:hover {
	border:solid 1px #666666;
}

.error_field {
	padding:5px;
	margin-bottom:3px;
	margin-top:3px;
	border: 1px solid red;
	background: yellow;
	width: 300px;
}

.footer_info {
	color:#6f8dA1;
}

/* Content Elements: Messages */

.message, .warning, .success, .error {
	width: 690px;
	_width: 700px;
	padding: 5px;
	margin-bottom: 3px;
	font-size: 12px;
}

.message {
	color: #ffffff;
	background-color: #aaa9a6;
}

.warning {
	color: #ffffff;
	background-color: #ff9900;
}

.success {
	color: #ffffff;
	background-color: #009000;
}

.error {
	color: #ffffff;
	background-color: #900000;
}

Passo 3 – Instalação da Library e do Helper Message

Para facilitar o envio e a exibição de mensagens para o usuário, encontrei esta prática biblioteca chamada Message no Wiki (confira) do Codeigniter, e no fórum encontrei um Helper (confira) para reduzir a exibição de código nas views.

Crie o arquivo application/libraries/messages.php com o conteúdo abaixo:

<?php if (!defined('BASEPATH')) exit('No direct script access allowed');

/**
* Message:: a class for writing feedback message information to the session
*
* Copyright 2006 Vijay Mahrra &amp; Sheikh Ahmed <webmaster@designbyfail.com>
*
* See the enclosed file COPYING for license information (LGPL).  If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* @author  Vijay Mahrra &amp; Sheikh Ahmed <webmaster@designbyfail.com>
* @url http://www.designbyfail.com/
* @version 1.0
*/

class Messages
{
    var $_ci;
    var $_types = array('success', 'error', 'warning', 'message');

    function Messages($params = array())
    {
        $this->_ci =&amp; get_instance();
        $this->_ci->load->library('session');
        // check if theres already messages, if not, initialise the messages array in the session
        $messages = $this->_ci->session->userdata('messages');
        if (empty($messages)) {
            $this->clear();
        }
    }

    // clear all messages
    function clear()
    {
        $messages = array();
        foreach ($this->_types as $type) {
            $messages[$type] = array();
        }
        $this->_ci->session->set_userdata('messages', $messages);
    }

    // add a message, default type is message
    function add($message, $type = 'message')
    {
        $messages = $this->_ci->session->userdata('messages');
        // handle PEAR errors gracefully
        if (is_a($message, 'PEAR_Error')) {
            $message = $message->getMessage();
            $type = 'error';
        } else if (!in_array($type, $this->_types)) {
            // set the type to message if the user specified a type that's unknown
            $type = 'message';
        }
        // don't repeat messages!
        if (!in_array($message, $messages[$type]) &amp;&amp; is_string($message)) {
            $messages[$type][] = $message;
        }
        $messages = $this->_ci->session->set_userdata('messages', $messages);
    }

    // return messages of given type or all types, return false if none
    function sum($type = null)
    {
        $messages = $this->_ci->session->userdata('messages');
        if (!empty($type)) {
            $i = count($messages[$type]);
            return $i;
        }
        $i = 0;
        foreach ($this->_types as $type) {
            $i += count($messages[$type]);
        }
        return $i;
    }

    // return messages of given type or all types, return false if none, clearing stack
    function get($type = null)
    {
        $messages = $this->_ci->session->userdata('messages');
        if (!empty($type)) {
            if (count($messages[$type]) == 0) {
                return false;
            }
            return $messages[$type];
        }
        // return false if there actually are no messages in the session
        $i = 0;
        foreach ($this->_types as $type) {
            $i += count($messages[$type]);
        }
        if ($i == 0) {
            return false;
        }

        // order return by order of type array above
        // i.e. success, error, warning and then informational messages last
        foreach ($this->_types as $type) {
            $return[$type] = $messages[$type];
        }
        $this->clear();
        return $return;
    }
}

Crie o arquivo application/helpers/msg_helper.php com o conteúdo abaixo:

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

if ( ! function_exists('output_msg')) {
    function output_msg($type = null) {
        $CI =&amp; get_instance();

        if ($CI->messages->sum($type) > 0) {
        $messages = $CI->messages->get($type);
        // display all messages of the type
        if (is_array($messages)) {
            $output = '';
            foreach ($messages as $type => $msgs) {
                if (count($msgs) > 0) {
                    $output .= '<div class=&quot;' . $type . '&quot;>';
                    $output .= '<ul>';
                    foreach ($msgs as $message) {
                        $output .= $message;
                    }
                    $output .= '</ul>';
                    $output .= '</div>';
                }
            }
        }
        return $output;
        }
    }
}

Passo 4 – Criação do Model

O model anêmico abaixo (que está mais para um Active Record Layer), incrivelmente reusável, foi inspirado no copiado deste exemplo de CRUD. Controller gordo + Model magro é considerado um anti-pattern, mas eu gosto que as coisas estejam distribuídas assim, e o CodeIgniter, pelo que vi no manual, também. (O que você acha?)

Arquivo application/models/user_model.php

<?php
class User_model extends Model {

    private $table = 'users';

    function User_model() {
        parent::Model();
    }

    function list_all($order_by = 'id') {
        $this->db->order_by($order_by,'asc');
        return $this->db->get($table);
    }

    function count_all() {
        return $this->db->count_all($this->table);
    }

    function get_paged_list($limit = 10, $offset = 0, $order_by = 'id') {
        $this->db->order_by($order_by,'asc');
        return $this->db->get($this->table, $limit, $offset);
    }

    function get_by_id($id) {
        $this->db->where('id', $id);
        return $this->db->get($this->table);
    }

    function save($obj) {
        $this->db->insert($this->table, $obj);
        return $this->db->insert_id();
    }

    function update($id, $obj) {
        $this->db->where('id', $id);
        $this->db->update($this->table, $obj);
    }

    function delete($id) {
        $this->db->where('id', $id);
        $this->db->delete($this->table);
    }
}
?>

Passo 5 – Criação da view para listagem

Nesta view é exibida a listagem paginada. Note a simplicidade da exibição de mensagens através da função output_msg, do Helper que instalamos acima.

Arquivo application/views/user_list.php

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<title>User List</title>
	<link href="<?php echo base_url(); ?>web/css/style.css" rel="stylesheet" type="text/css" />
	<script>
		function remove(id) {
			if (confirm("Are you sure?")) {
				window.location = '<?php echo base_url(); ?>user/delete/'+id;
			}
		}
	</script>
</head>
<body>
<?php
	// show user messages sent by controller
	echo output_msg($type = null);
?>
<div id="container">
	<h1>USER LIST</h1>
	<table id="listing" cellspacing=0>
		<tr>
			<th width=45%>Name</th>
			<th width=45%>E-mail</th>
			<th width=10%>Actions</th>
		</tr>
		<?php foreach($list as $user): ?>
			<tr>
				<td><?php echo $user->name; ?></td>
				<td><?php echo $user->email; ?></td>
				<td align="center">
					<a href="<?php echo base_url(); ?>user/prepareUpdate/<?php echo $user->id; ?>"><img src="<?php echo base_url(); ?>web/i/icon/b_edit.png" border=0 title="Edit"></a>
					<a href="#" onclick="remove(<?php echo $user->id; ?>);"><img src="<?php echo base_url(); ?>web/i/icon/b_drop.png" border=0 title="Delete"></a>
				</td>
			</tr>
		<?php endforeach ?>
	</table>
	<div style="padding:10px;"><?php echo $pagination; ?><br clear="all" /></div>
	<input type=button onclick="location.href='<?php echo base_url(); ?>user/prepareInsert/';" value="New User" class="buttonAdmin">
</div>
<br /><br />
<p class="footer_info">
	Total execution time: <?php echo $this->benchmark->elapsed_time() ?> seconds;
	Memory usage: <?php echo $this->benchmark->memory_usage() ?>
</p>
</body>
</html>

Passo 6 – Criação da view contendo o formulário de cadastro e edição

Esta view contém o formulário que será exibido para criar e editar usuários. Observe o uso da função set_value() e da variável $values, que contém as informações para popular os campos com os dados do item.

Arquivo application/views/user_form.php

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="content-type" content="text/html; charset=utf-8" />
	<title><?php echo $title; ?></title>
	<link href="<?php echo base_url(); ?>web/css/style.css" rel="stylesheet" type="text/css" />
</head>
<body>
<?php
	// show user messages sent by controller
	echo output_msg($type = null);
?>
<form method="post" action="<?php echo $action; ?>">
	<div id="container">
		<h1><?php echo $title; ?></h1>
				<label>Name*</label><br>
				<input type="text" name="name" value="<?php echo set_value('name', $values['name']); ?>" style="width:500px;" maxlength="100">
				<?php echo form_error('name'); ?><br><br>

				<label>E-mail*</label><br>
				<input type="text" name="email" value="<?php echo set_value('email', $values['email']); ?>" style="width:500px;" maxlength="100">
				<?php echo form_error('email'); ?><br><br>

				<input type=submit value=" Save ">

				<input type=button onclick="location.href='<?php echo base_url(); ?>user/index/';" value="Back to listing" class="buttonAdmin" style="margin-left:20px;">
	</div>
	<input type=hidden name=id value="<?php echo set_value('id', $values['id']); ?>">
</form>
<br /><br />
<p class="footer_info">
	Total execution time: <?php echo $this->benchmark->elapsed_time() ?> seconds;
	Memory usage: <?php echo $this->benchmark->memory_usage() ?>
</p>
</body>
</html>

Passo 7 – O Controller

O controller começa com duas variáveis de configuração: quantidade de linhas exibidas por página na listagem ($limit) e nome da coluna que determinará a ordenação da listagem ($order_by).

No construtor, carregamos as libraries e helpers necessários.

Entenda o papel de cada método:

O index() prepara os dados da paginação, invoca os dados do modelo e transfere esses dados para a view user_list.php.

O prepareInsert() monta um formulário de cadastro em branco.

O prepareUpdate() consulta o modelo e monta um formulário preenchido com os dados do $id solicitado.

O insert() e o update() são apenas chamadas para o _save(). Foram criados assim para facilitar a visualização e compreensão do fluxo da aplicação.

O método privado _save() cria ou edita um item, de acordo com a $action solicitada. Nesse método são configuradas as regras de validação dos dados vindos do formulário.

O delete() exclui o item com o $id informado, e retorna para a listagem de usuários.

Arquivo application/controllers/user.php

<?php
	class User extends Controller {

		// records per page
		private $limit = 5;

		// column to order by at listing
		private $order_by = 'name';

		function User()  {
			parent::Controller();

			$this->load->database();
			$this->load->library('form_validation');
			$this->load->helper('url');

			// send and show messages to user
			$this->load->library('messages');
			$this->load->helper('msg');

			$this->load->model('user_model', '', TRUE);

			$this->output->enable_profiler($this->config->item('profiler'));
		}

		function index($offset = 0) {
			$data = array();

			// http://.../ci_sandbox(1)/index(2)/<offset>(3)
			$uri_segment = 3;
			// where this page begins
			$offset = $this->uri->segment($uri_segment);
			// load data list
			$data['list'] = $this->user_model->get_paged_list($this->limit, $offset, $this->order_by)->result();

			// generate pagination
			$this->load->library('pagination');
			$config['base_url'] = site_url('user/index/');
	 		$config['total_rows'] = $this->user_model->count_all();
	 		$config['per_page'] = $this->limit;
			$config['uri_segment'] = $uri_segment;
			$this->pagination->initialize($config);
			$data['pagination'] = $this->pagination->create_links();

			$this->load->view('user_list',$data);
		}

		function prepareInsert() {
			// set validation properties
			$data['values'] = NULL;

			// set common properties
			$data['title'] = 'NEW USER';
			$data['action'] = site_url('user/insert');

			$this->load->view('user_form', $data);
		}

		function prepareUpdate($id) {
			// prefill form values
			$obj = $this->user_model->get_by_id($id)->row();
			$data['values']['id'] = $id;
			$data['values']['name'] = $obj->name;
			$data['values']['email'] = $obj->email;

			// set common properties
			$data['title'] = 'EDIT USER';
			$data['action'] = site_url('user/update');

			$this->load->view('user_form', $data);
		}

		function insert() {
			$this->_save('insert');
		}

		function update() {
			$this->_save('update');
		}

		function _save($action = 'insert') {
			// set common properties
			if ($action == 'update') {
				$data['title'] = 'EDIT USER';
				$data['action'] = site_url('user/update');
			} else {
				$data['title'] = 'NEW USER';
				$data['action'] = site_url('user/insert');
			}

			// set validation properties
			$this->form_validation->set_rules('id','','');
			$this->form_validation->set_rules('name','Name','trim|required|max_length[100]|xss_clean');
			$this->form_validation->set_rules('email','E-mail','trim|required|max_length[100]|valid_email|xss_clean');
			$this->form_validation->set_error_delimiters('<p class="error_field">', '</p>');

			// run validation
			if ($this->form_validation->run() == FALSE) {
				$data['values'] = NULL;

				// set user message
				$this->messages->add('Important: please see the itens below.', 'warning');

				$this->load->view('user_form', $data);
			} else {

				$obj->name = $this->input->post('name');
				$obj->email = $this->input->post('email');

				if ($action == 'update') {
					$id = $this->input->post('id');
					$this->user_model->update($id, $obj);
					$this->messages->add('User updated', 'success'); // ser user message
				} else {
					$id = $this->user_model->save($obj);
					$this->messages->add('User created', 'success'); // set user message
				}

				// redirect to list page
				redirect('user/index/','refresh');
			}
		}

		function delete($id) {
			$this->user_model->delete($id);

			// set user message
			$this->messages->add('User removed', 'success');

			// redirect to list page
			redirect('user/index/','refresh');
		}

	}
?>

Espero que esse exemplo simples seja útil para quem está iniciando, e que os mais experientes comentem e sugiram melhorias.

2 já comentaram sobre “CRUD Básico com CodeIgniter 1.7.2”

  1. Bem interessante seu exemplo, parabéns, poste mais sobre CI.

  2. Olá, bem legal o seu post.

    Estou tendo problemas ao tentar rodá-lo. Recebo esta mensagem:
    “Internal Server Error
    The server encountered an internal error or misconfiguration and was unable to complete your request.”

    Eu já tenho um exemplinho bem simples com codeigniter e mysql rodando aqui, então acho que meu ambiente deveria estar ok.

    Alguma dica do que pode estar acontecendo?

    Valeu.
    Humberto

Leave a Reply

Lomadee, uma nova espécie na web. A maior plataforma de afiliados da América Latina