CRUD Codeigniter – Vue.js – Bootstrap
Es hora de actualizarse y usar los nuevos frameworks dentro de las nuevas tendencias de programación, en esta oportunidad realizaremos un CRUD con CodeIgniter y Vue.js para ser más específico usare la versión de CI 3.1.6 y el framework progresivo de Vue.js, al utilizar Vue.js consumiremos recursos de la interfaz del lado cliente o usuario, ahora vayamos a ver el código fuente de este CRUD Codeigniter – Vue.js – Bootstrap. Como estoy haciendo uso de una versión de CI 3.1.6 con el tiempo aparecerán otras, pero como es un aplicación CRUD, no habrá mucha diferencia al momento de actualizar de versión.
Configuración
En la dirección: application/controllers crear un archivo User.php la primera letra con mayúscula, antes de continuar en application/controllers/config/config.php agregar la dirección de tu aplicación y en application/controllers/config/database.php agregar tus datos de conexión, el script de la base de datos lo encontraras en última parte de este artículo.
Base de datos
Ejecuta este script en phpmyadmin. Antes de instalar debes realizar las dos configuraciones en config.php, patabase.php y app.js.
<?php
/*
| -----------------------------------------------------
| PROYECTO: PHP CRUD usando PDO y Bootstrap
| -----------------------------------------------------
| AUTOR: ANTHONCODE
| -----------------------------------------------------
| FACEBOOK: FACEBOOK.COM/ANTHONCODE
| -----------------------------------------------------
| COPYRIGHT: AnthonCode
| -----------------------------------------------------
| WEBSITE: https://anthoncode.com/
| -----------------------------------------------------
*/
defined('BASEPATH') OR exit('No direct script access allowed');
class User extends CI_Controller{
function __construct(){
parent::__construct();
$this->load->model('user_model','user');
}
public function index(){
$this->load->view('template/header');
$this->load->view('users/index');
$this->load->view('template/footer');
}
public function showAll(){
$query= $this->user->showAll();
if($query){
$result['users'] = $this->user->showAll();
}
echo json_encode($result);
}
public function addUser(){
$config = array(
array('field' => 'firstname',
'label' => 'Firstname',
'rules' => 'trim|required'
),
array('field' => 'lastname',
'label' => 'Lastname',
'rules' => 'trim|required'
),
array('field' => 'gender',
'label' => 'Gender',
'rules' => 'required'
),
array('field' => 'birthday',
'label' => 'Birthday',
'rules' => 'trim|required'
),
array('field' => 'email',
'label' => 'Email',
'rules' => 'trim|required'
),
array(
'field' => 'contact',
'label' => 'Contact',
'rules' => 'trim|required'
),
array(
'field' => 'address',
'label' => 'Address',
'rules' => 'trim|required'
)
);
$this->form_validation->set_rules($config);
if ($this->form_validation->run() == FALSE) {
$result['error'] = true;
$result['msg'] = array(
'firstname'=>form_error('firstname'),
'lastname'=>form_error('lastname'),
'gender'=>form_error('gender'),
'birthday'=>form_error('birthday'),
'email'=>form_error('email'),
'contact'=>form_error('contact'),
'address'=>form_error('address')
);
}else{
$data = array(
'firstname'=> $this->input->post('firstname'),
'lastname'=> $this->input->post('lastname'),
'gender'=> $this->input->post('gender'),
'birthday'=> $this->input->post('birthday'),
'email'=> $this->input->post('email'),
'contact'=> $this->input->post('contact'),
'address'=> $this->input->post('address')
);
if($this->user->addUser($data)){
$result['error'] = false;
$result['msg'] ='Usuario agregado correctamente';
}
}
echo json_encode($result);
}
public function updateUser(){
$config = array(
array('field' => 'firstname',
'label' => 'Firstname',
'rules' => 'trim|required'
),
array('field' => 'lastname',
'label' => 'Lastname',
'rules' => 'trim|required'
),
array('field' => 'gender',
'label' => 'Gender',
'rules' => 'required'
),
array('field' => 'birthday',
'label' => 'Birthday',
'rules' => 'trim|required'
),
array('field' => 'email',
'label' => 'Email',
'rules' => 'trim|required'
),
array(
'field' => 'contact',
'label' => 'Contact',
'rules' => 'trim|required'
),
array(
'field' => 'address',
'label' => 'Address',
'rules' => 'trim|required'
)
);
$this->form_validation->set_rules($config);
if ($this->form_validation->run() == FALSE) {
$result['error'] = true;
$result['msg'] = array(
'firstname'=>form_error('firstname'),
'lastname'=>form_error('lastname'),
'gender'=>form_error('gender'),
'birthday'=>form_error('birthday'),
'email'=>form_error('email'),
'contact'=>form_error('contact'),
'address'=>form_error('address')
);
}else{
$id = $this->input->post('id');
$data = array(
'firstname'=> $this->input->post('firstname'),
'lastname'=> $this->input->post('lastname'),
'gender'=> $this->input->post('gender'),
'birthday'=> $this->input->post('birthday'),
'email'=> $this->input->post('email'),
'contact'=> $this->input->post('contact'),
'address'=> $this->input->post('address')
);
if($this->user->updateUser($id,$data)){
$result['error'] = false;
$result['success'] = 'Usuario actualizado correctamente';
}
}
echo json_encode($result);
}
public function deleteUser(){
$id = $this->input->post('id');
if($this->user->deleteUser($id)){
$msg['error'] = false;
$msg['success'] = 'Usuario eliminado correctamente';
}else{
$msg['error'] = true;
}
echo json_encode($msg);
}
public function searchUser(){
$value = $this->input->post('text');
$query = $this->user->searchUser($value);
if($query){
$result['users']= $query;
}
echo json_encode($result);
}
}
En la dirección application/views crear una carpeta no el nombre de template y crear dos archivos uno llamado header.php y el otro footer.php (no es necesario mayúsculas)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Codeigniter + Vue JS</title>
<link rel="icon" href="<?php echo base_url()?>assets/img/civue.png">
<link rel="stylesheet" href="<?php echo base_url()?>assets/css/bulma.min.css">
<link rel="stylesheet" href="<?php echo base_url()?>assets/css/bootstrap.min.css">
<link rel="stylesheet" href="<?php echo base_url()?>assets/css/animate.min.css">
<link rel="stylesheet" href="<?php echo base_url()?>assets/css/font-awesome.min.css">
<link rel="stylesheet" href="<?php echo base_url()?>assets/css/style.css">
<script src="<?php echo base_url()?>assets/js/vue.min.js"></script>
<script src="<?php echo base_url()?>assets/js/axios.min.js"></script>
<script src="<?php echo base_url()?>assets/js/jquery.min.js"></script>
</head>
<body class="bg-light">
</body>
<script src="<?php echo base_url();?>assets/js/pagination.js"></script>
<script src="<?php echo base_url();?>assets/js/app.js"></script>
</html>
En la dirección application/views crear una carpeta con el nombre de users y crear dos archivos uno llamado index.php y modal.php, en el archivo index se encontrará el script para visualizar la interfaz principal de la aplicación, en el archivo modal.php se encuentra las líneas de código para agregar, editar y eliminar.
<ul class="nav justify-content-center bg-dark text-light">
<li class="nav-item">
<a class="nav-link text-white h4" href="<?php echo base_url();?>user">Codeigniter + Vue JS - AnthonCode<img src="<?php echo base_url();?>assets/img/civue.png" width="60" height="70"></a>
<a class="nav-link text-white h5 text-center" href="http://facebook.com/anthoncode" target="_blank"><i class="fa fa-facebook-official"></i> AnthonCode</a>
</li>
</ul>
<div id="app">
<div class="container">
<div class="row">
<transition
enter-active-class="animated fadeInLeft"
leave-active-class="animated fadeOutRight">
<div class="notification is-success text-center px-5 top-middle" v-if="successMSG" @click="successMSG = false">{{successMSG}}</div>
</transition>
<div class="col-md-12">
<table class="table bg-dark my-3">
<tr>
<td> <button class="btn btn-default btn-block" @click="addModal= true">Nuevo</button></td>
<td><input placeholder="Search"type="search" class="form-control" v-model="search.text" @keyup="searchUser" name="search"></td>
</tr>
</table>
<table class="table is-bordered is-hoverable">
<thead class="text-white bg-dark" >
<th class="text-white">ID</th>
<th class="text-white">Nombre</th>
<th class="text-white">Apellido</th>
<th class="text-white">Email</th>
<th class="text-white">Teléfono</th>
<th class="text-white">Dirección</th>
<th class="text-white">Sexo</th>
<th colspan="2" class="text-center text-white">Acción</th>
</thead>
<tbody class="table-light">
<tr v-for="user in users" class="table-default">
<td>{{user.id}}</td>
<td>{{user.firstname}}</td>
<td>{{user.lastname}}</td>
<td>{{user.email}}</td>
<td>{{user.contact}}</td>
<td>{{user.address}}</td>
<td>
<img :src="imgGender(user.gender)" width='50' height="50">
</td>
<td><button class="btn btn-info fa fa-edit" @click="editModal = true; selectUser(user)"></button></td>
<td><button class="btn btn-danger fa fa-trash" @click="deleteModal = true; selectUser(user)"></button></td>
</tr>
<tr v-if="emptyResult">
<td colspan="9" rowspan="4" class="text-center h1">No Record Found</td>
</tr>
</tbody>
</table>
</div>
</div>
<pagination
:current_page="currentPage"
:row_count_page="rowCountPage"
@page-update="pageUpdate"
:total_users="totalUsers"
:page_range="pageRange"
>
</pagination><br>
</div>
<?php include 'modal.php';?>
</div>
<!--agregar modal-->
<modal v-if="addModal" @close="clearAll()">
<h3 slot="head" >Agregar usuario</h3>
<div slot="body" class="row">
<div class="col-md-6">
<div class="form-group">
<label>Nombre</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.firstname}" name="firstname" v-model="newUser.firstname">
<div class="has-text-danger" v-html="formValidate.firstname"> </div>
</div>
<div class="form-group">
<label>Apellido</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.lastname}" name="lastname" v-model="newUser.lastname">
<div class="has-text-danger" v-html="formValidate.lastname"> </div>
</div>
<div class="form-group">
<label for="">Sexo</label><br>
<div class="btn-group">
<button class="btn btn-outline-dark fa fa-mars" :class="{'active':(newUser.gender == 'boy')}" @click.prevent="pickGender('boy')"> Masculino</button>
<button class="btn btn-outline-dark fa fa-venus" :class="{'active': (newUser.gender == 'girl')}" @click.prevent="pickGender('girl')"> Femenino</button>
</div>
<div class="has-text-danger"v-html="formValidate.gender"></div>
</div>
<div class="form-group">
<label>Cumpleaños</label>
<input type="date" class="form-control" :class="{'is-invalid': formValidate.birthday}" name="birthday" v-model="newUser.birthday">
<div class="has-text-danger" v-html="formValidate.birthday"> </div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.email}" name="email" v-model="newUser.email">
<div class="has-text-danger" v-html="formValidate.email"></div>
</div>
<div class="form-group">
<label>Contacto</label>
<input type="text" class="form-control":class="{'is-invalid': formValidate.contact}" name="contact" v-model="newUser.contact">
<div class="has-text-danger" v-html="formValidate.contact"> </div>
</div>
<div class="form-group">
<label>Dirección</label>
<textarea cols="35" rows="5" :class="{'is-invalid': formValidate.address}" name="address" v-model="newUser.address" class="form-control"></textarea>
<div class="has-text-danger" v-html="formValidate.address"> </div>
</div>
</div>
</div>
<div slot="foot">
<button class="btn btn-dark" @click="addUser">Agregar</button>
</div>
</modal>
<!--actualizar modal-->
<modal v-if="editModal" @close="clearAll()">
<h3 slot="head" >Editar Usuario</h3>
<div slot="body" class="row">
<div class="col-md-6">
<div class="form-group">
<label>Nombre</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.firstname}" name="firstname" v-model="chooseUser.firstname">
<div class="has-text-danger" v-html="formValidate.firstname"> </div>
</div>
<div class="form-group">
<label>Apellido</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.lastname}" name="lastname" v-model="chooseUser.lastname">
<div class="has-text-danger" v-html="formValidate.lastname"> </div>
</div>
<div class="form-group">
<label for="">Sexo</label><br>
<div class="btn-group">
<button class="btn btn-outline-dark fa fa-mars" :class="{'active':(chooseUser.gender == 'boy')}" @click="changeGender('boy')"> Masculino</button>
<button class="btn btn-outline-dark fa fa-venus" :class="{'active': (chooseUser.gender == 'girl')}" @click="changeGender('girl')"> Femenino</button>
</div>
<div class="has-text-danger"v-html="formValidate.gender"></div>
</div>
<div class="form-group">
<label>Cumpleaños</label>
<input type="date" class="form-control" :class="{'is-invalid': formValidate.birthday}" name="birthday" v-model="chooseUser.birthday">
<div class="has-text-danger" v-html="formValidate.birthday"> </div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" :class="{'is-invalid': formValidate.email}" name="email" v-model="chooseUser.email">
<div class="has-text-danger" v-html="formValidate.email"></div>
</div>
<div class="form-group">
<label>Contacto</label>
<input type="text" class="form-control":class="{'is-invalid': formValidate.contact}" name="contact" v-model="chooseUser.contact">
<div class="has-text-danger" v-html="formValidate.contact"> </div>
</div>
<div class="form-group">
<label>Dirección</label>
<textarea cols="35" rows="5" :class="{'is-invalid': formValidate.address}" name="address" v-model="chooseUser.address" class="form-control"></textarea>
<div class="has-text-danger" v-html="formValidate.address"> </div>
</div>
</div>
</div>
<div slot="foot">
<button class="btn btn-dark" @click="updateUser">Actualizar</button>
</div>
</modal>
<!--eliminar modal-->
<modal v-if="deleteModal" @close="clearAll()">
<h3 slot="head">Elimimar</h3>
<div slot="body" class="text-center">¿Estas seguro de eliminar estos datos?</div>
<div slot="foot">
<button class="btn btn-dark" @click="deleteModal = false; deleteUser()" >Eliminar</button>
<button class="btn" @click="deleteModal = false">Cancelar</button>
</div>
</modal>
Ahora en la carpeta assets agregar los archivos de bootstrap, jquery y vue.js, ordenados en carpetas separadas, también se debe encontrar los archivos axios.min.js, jquery, vue.js y los nuevos archivos que crearemos, en la carpeta js dentro de assets agregar app.js toda las operaciones del CRUD se encuentran aquí, Nota, en la línea de código 28 agregar la dirección de tu aplicación, en mi caso hago uso de ‘http://localhost:8080/supercrud/civuejs/‘ (debe coincidir con la dirección de config.php)
Vue.component('modal',{ //modal
template:`
<transition
enter-active-class="animated rollIn"
leave-active-class="animated rollOut">
<div class="modal is-active" >
<div class="modal-card border border border-secondary">
<div class="modal-card-head text-center bg-dark">
<div class="modal-card-title text-white">
<slot name="head"></slot>
</div>
<button class="delete" @click="$emit('close')"></button>
</div>
<div class="modal-card-body">
<slot name="body"></slot>
</div>
<div class="modal-card-foot" >
<slot name="foot"></slot>
</div>
</div>
</div>
</transition>
`
})
var v = new Vue({
el:'#app',
data:{
url:'http://localhost:8080/supercrud/civuejs/',
addModal: false,
editModal:false,
deleteModal:false,
users:[],
search: {text: ''},
emptyResult:false,
newUser:{
firstname:'',
lastname:'',
gender:'',
birthday:'',
email:'',
contact:'',
address:''},
chooseUser:{},
formValidate:[],
successMSG:'',
//pagination
currentPage: 0,
rowCountPage:5,
totalUsers:0,
pageRange:2
},
created(){
this.showAll();
},
methods:{
showAll(){ axios.get(this.url+"user/showAll").then(function(response){
if(response.data.users == null){
v.noResult()
}else{
v.getData(response.data.users);
}
})
},
searchUser(){
var formData = v.formData(v.search);
axios.post(this.url+"user/searchUser", formData).then(function(response){
if(response.data.users == null){
v.noResult()
}else{
v.getData(response.data.users);
}
})
},
addUser(){
var formData = v.formData(v.newUser);
axios.post(this.url+"user/addUser", formData).then(function(response){
if(response.data.error){
v.formValidate = response.data.msg;
}else{
v.successMSG = response.data.msg;
v.clearAll();
v.clearMSG();
}
})
},
updateUser(){
var formData = v.formData(v.chooseUser); axios.post(this.url+"user/updateUser", formData).then(function(response){
if(response.data.error){
v.formValidate = response.data.msg;
}else{
v.successMSG = response.data.success;
v.clearAll();
v.clearMSG();
}
})
},
deleteUser(){
var formData = v.formData(v.chooseUser);
axios.post(this.url+"user/deleteUser", formData).then(function(response){
if(!response.data.error){
v.successMSG = response.data.success;
v.clearAll();
v.clearMSG();
}
})
},
formData(obj){
var formData = new FormData();
for ( var key in obj ) {
formData.append(key, obj[key]);
}
return formData;
},
getData(users){
v.emptyResult = false; // se vuelve falso si tiene un registro
v.totalUsers = users.length // obtiene un total de usuarios
v.users = users.slice(v.currentPage * v.rowCountPage, (v.currentPage * v.rowCountPage) + v.rowCountPage); //slice the result for pagination
//si el registro está vacío, retrocede una página
if(v.users.length == 0 && v.currentPage > 0){
v.pageUpdate(v.currentPage - 1)
v.clearAll();
}
},
selectUser(user){
v.chooseUser = user;
},
clearMSG(){
setTimeout(function(){
v.successMSG=''
},3000); // desapareciendo el mensaje exitoso en 2 segundos
},
clearAll(){
v.newUser = {
firstname:'',
lastname:'',
gender:'',
birthday:'',
email:'',
contact:'',
address:''};
v.formValidate = false;
v.addModal= false;
v.editModal=false;
v.deleteModal=false;
v.refresh()
},
noResult(){
v.emptyResult = true; // se convierte en verdadero si el registro está vacío, imprime 'No Record Found'
v.users = null
v.totalUsers = 0 // eliminar la página actual si está vacía
},
pickGender(gender){
return v.newUser.gender = gender // agregar nuevo usuario con la selección de género
},
changeGender(gender){
return v.chooseUser.gender = gender // actualizar el género
},
imgGender(value){
return v.url+'assets/img/gender_'+value+'.png' // para el signo de género de imagen en la tabla
},
pageUpdate(pageNumber){
v.currentPage = pageNumber; // recibir el número de la página actual provino de la plantilla de paginación
v.refresh()
},
refresh(){
v.search.text ? v.searchUser() : v.showAll(); // para prevenir
}
}
})
también debemos agregar pagination.js en la carpeta js, esto para mostrar datos en otra página.
Vue.component('pagination',{
template:` <div class="pager" v-if="totalPages > 0">
<ul class="pagination justify-content-center">
<li @click="updatePage(prev)" class="page-item">
<a v-if="showPrevLink"class="page-link fa fa-arrow-left" href="javascript:void(0)" aria-label="Anterior">
</a>
</li>
<li class="page-item" v-if="firstPage">
<a class="page-link" @click="updatePage(0)" href="javascript:void(0)">1</a>
</li>
<li class="page-item page-link text-dark px-3 fa fa-ellipsis-h" v-if="firstPage"></li>
<li class="page-item" :class="{'active': current_page == page}"v-for="page in pages">
<a class="page-link" @click="updatePage(page)" href="javascript:void(0)">{{page + 1}}</a>
</li>
<li class="page-item page-link text-dark px-3 fa fa-ellipsis-h" v-if="lastPage"></li>
<li class="page-item" v-if="lastPage">
<a class="page-link" @click="updatePage(totalPages - 1)" href="javascript:void(0)">{{totalPages}}</a>
</li>
<li @click="updatePage(next)" class="page-item">
<a v-if="showNextLink"class="page-link fa fa-arrow-right" href="javascript:void(0)" aria-label="Siguiente">
</a>
</li>
</ul>
</div>`,
props:['current_page', 'row_count_page','total_users','page_range'],
computed:{
prev(){
return this.current_page - 1
},
next(){
return this.current_page + 1
},
rangeStart(){
var start = this.current_page - this.page_range
return (start > 0) ? start : 0;
},
rangeEnd(){
var end = this.current_page + this.page_range
return (end < this.totalPages) ? end : this.totalPages
},
pages(){
var pages = []
for(var i = this.rangeStart; i < this.rangeEnd; i++){
pages.push(i)
}
return pages
},
totalPages(){
return Math.ceil(this.total_users / this.row_count_page);
},
firstPage(){
return this.rangeStart !==0
},
lastPage(){
return this.rangeEnd < this.totalPages
},
showPrevLink() {
return this.current_page == 0 ? false : true;
},
showNextLink() {
return this.current_page == (this.totalPages - 1) ? false : true;
}
},
methods: {
updatePage(pageNumber) {
this.$emit('page-update', pageNumber);
},
}
})
Base de datos
Ejecuta este script en phpmyadmin. Antes de instalar debes realizar las dos configuraciones en config.php, database.php y app.js.
11 comentarios
mucho de lo que vi aquí me sirvió muchísimo, gracias que buen tutorial
Buenas, estoy guiándome de su codigo para usarlo con una tabla de la base de datos de mi proyecto, pero me falla el renderizado diciendome que esta mal el metodo pageUpdate y no carga la data cuando aplico el axios, jamas la muestra, no se que más debo revisar, que archivos de configuracion debo arreglar
¿Ya agregaste tu dirección de dominio?
Si ya hice eso parece que el template falla, me bota errores como: Property or method «pageUpdate» is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
(found in root instance)
No se si puedo compartirle mi código por Github para poder chequearlo mejor, lo he revisado al revés y al derecho y no sé que estoy haciendo mal
Buenas, estoy aprendiendo acerca de hacer un crud, y lo estoy intentando con tu codigo, pero seguí todos los pasos, y al momento de probarlo, no realiza la conexión a la base de datos, sabras por que?
Revisa la conexión del dominio, yo trabaje con xampp php5.6.38
Pues finalmente lo que hice es método, que realiza la operación de este modo:
» createdFormData : function () {
var formDa = new FormData();
for(var key in this.newUser){
formDa.append(key, this.newUser[key]);
}
return formDa;
},»
Lo que aun no entiendo es, que no me funcione directamente en el método adduser. Lo bueno es que si lo modifico para pasar como parámetro cualquier array me va ha funcionar lo que lo hace reutilizable.
Gracias Anthony, la dirección a la que apuntó es correcta, pero creo que ya he solucionado. En cuanto tenga un momento pongo aquí el detalle, pero básicamente no me funcionó la creación del FormData.
Antes de nada, gracias por el tutorial, es realmente interesante.
La cuestión es que me estoy encontrando con dificultades para mandar el post, concretamente en la linea 77:
var formData = v.formData(v.newUser);
Resulta que no se ejecuta si dejo esa linea, si la quito se ejecuta pero obviamente no envía nada, al post, he observado que tengo dificultades para lanzar js desde vue. ¿hay algo que sea necesario configurar? gracias.
En app.js en la línea de código 28 agregar la dirección de tu aplicación, en mi caso hago uso de ‘http://localhost:8080/supercrud/civuejs/‘ (debe coincidir con la dirección de config.php)