Creando un sistema CRUD con Vue y Laravel 23/23

serie-crud-js

23- Completando el CRUD

Habíamos dicho en el post anterior que el código que nos quedaba era prácticamente trivial pero ya había sido largo como para poner todo alli.

Solo nos queda enfrentarnos a un problema de lógica mirando más de cerca el watch y específicamente una acción que tenemos preparada desde hace varios posts.

idFilterDeparture: function (value) {
    let me = this;
    this.filterDeparture.map(function (x) {
        if (x.id === value) {
            me.filterPosition = x.positions;
            me.idFilterPosition = me.filterPosition[0].id;
        }
    });
}

Esto lo tenemos en el watch y es sencillo. Cada vez que cambia idFilterDeparture (que es el valor seleccionado del select de departamentos) actualiza el select de cargos con los valores relacionados y posiciona el select en el primer elemento. Cuando vayamos a abrir el modal para actualizar o borrar necesitaremos que el cargo elegido no sea el primero de la lista sino que sea el cargo relacionado con  Empleado.

Por eso alteramos el  funcionamiento del watch con una variable.

El segundo problema se presenta cuando elegimos un empleado para editar por ejemplo,  cancelamos y lo volvemos a elegir el  select de Departamento relacionado tendrá el mismo valor que antes por lo que watch no funcionara.

Para el primer problema lo solucionaremos creando una  variable nueva llamada nowatch en data con valor 0

nowatch: 0,

y modificaremos el watch así

idFilterDeparture: function (value) {
    let me = this;
    this.filterDeparture.map(function (x) {
        if (x.id === value) {
            me.filterPosition = x.positions;
            if (!me.nowatch) {
                me.idFilterPosition = me.filterPosition[0].id;
            } else {
                me.idFilterPosition = me.nowatch;

            }
        }
    });
    this.nowatch = 0;
}

No son grandes cambios. Simplemente ahora se toma en cuenta que cuando la variable nowatch no sea 0 se elija ese cargo y no el primero de la lista. Luego esa variable siempre se pone a cero por una razón lógica. Esa elección solo tiene que suceder una vez: Cuando se abre el modal para actualizar o eliminar luego el watch tiene que seguir funcionando como siempre.

Miremos ahora el openModal employee / delete:

case 'delete':
{
    this.modalGeneral = 1;
    this.titleModal = 'Eliminacion de Empleado';
    this.messageModal = 'Confirme los datos del Empleado';
    this.modalEmployee = 3;
    this.nameEmployee = data['name'];
    this.lastnameEmployee = data['lastname'];
    this.emailEmployee = data['email'];
    this.birthdayEmployee = data['birthday'];
    this.filterDeparture = [];
    this.filterPosition = [];
    this.idEmployee = data['id'];
    this.filterDeparture = this.departures;
    this.nowatch = data['position']['id'];
    this.idFilterDeparture = data['departure']['id'];
    break;
}

La mayoría de las variables ya las conocemos. Miremos las 3 últimas.

No se hace filtro de departamento. No es necesario ya que de toda la lista vamos a elegir uno específico y no se podrá modificar.

Cambiamos la variable nowatch para cuando el watch haga efecto y  guardamos el valor del cargo relacionado.

Cambiamos la variable idFilterDeparture guardando el valor del departamento relacionado. Este cambio de variable evoca el watch.

De esta manera se completarán ambos select con el valor relacionado de departamento y cargo.

Ahora el segundo problema con watch que comentamos arriba lo resolvemos agregando dos valores a closeModal:

this.nowatch = 0;
this.idFilterDeparture = 0;

La primera es solo poner a cero nowatch. Y la segunda es sirve para que si se cancela la acción y se vuelve a intentar eliminar el mismo empleado la variable cambie de valor y el watch actúe.

Completamos la función destroyEmployee:

destroyEmployee() {
    let me = this;
    axios.delete('{{url('/employee/delete')}}'+'/'+this.idEmployee)
        .then(function (response) {
            me.nameEmployee = '';
            me.lastnameEmployee = '';
            me.emailEmployee = '';
            me.birthdayEmployee = '';
            me.idFilterPosition = 0;
            me.errorEmployee = 0;
            me.errorMessageEmployee = [];
            me.modalEmployee = 0;
            me.closeModal();
        })
        .catch(function (error) {
            console.log(error);
        });
},

Como ya hemos hecho en los demás modelos y en el controlador:

public function delete($id)
{
    Employee::find($id)->delete();
}

Ahora vayamos a actualizar:

openModal employee / update

case 'update':
{
    this.modalGeneral = 1;
    this.titleModal = 'Modificacion de Empleado';
    this.messageModal = 'Cambie los datos del Empleado';
    this.modalEmployee = 2;
    this.nameEmployee = data['name'];
    this.lastnameEmployee = data['lastname'];
    this.emailEmployee = data['email'];
    this.birthdayEmployee = data['birthday'];
    this.filterDeparture = [];
    this.filterPosition = [];
    this.idEmployee = data['id'];
    let me = this;
    this.departures.map(function (x) {
        if (x.positions.length) {
            if (me.filterDeparture.indexOf(x)) me.filterDeparture.push(x);
        }
    });
    this.nowatch = data['position']['id'];
    this.idFilterDeparture = data['departure']['id'];
    break;
}

Aqui si hacemos filtro de los valores del select del departamento. ¿Porque? Lo explicamos es su momento pero podemos hacer memoria. Con este filtro estamos evitando departamentos que no tengan cargos. Los siguientes pasos son los mismos de eliminación. Actualizar la variable nowatch y seleccionar el departamento relacionado.

La función updateEmployee quedaría así:

updateEmployee() {
    if (this.validateEmployee()) {
        return;
    }
    let me = this;
    axios.put('{{route('employeeupdate')}}', {
                'id': this.idEmployee,
                'name': this.nameEmployee,
                'lastname': this.lastnameEmployee,
                'email': this.emailEmployee,
                'birthday': this.birthdayEmployee,
                'position': this.idFilterPosition
            })
        .then(function (response) {
            me.errorMessageEmployee = [];
            me.errorEmployee = 0;
            if (response.data.date) {
                me.errorEmployee = 1;
                me.errorMessageEmployee.push(response.data.date[0]);
            } else {
                me.nameEmployee = '';
                me.lastnameEmployee = '';
                me.emailEmployee = '';
                me.birthdayEmployee = '';
                me.idFilterPosition = 0;
                me.errorEmployee = 0;
                me.errorMessageEmployee = [];
                me.modalEmployee = 0;
                me.closeModal();
            }
        })
        .catch(function (error) {
            me.errorMessageEmployee = [];
            me.errorEmployee = 0;
            if (error.response && error.response.status === 500) {
                console.log(error.response.data)
            }
            if (error.response && error.response.status === 422) {
                me.errorEmployee = 1;
                me.errorMessageEmployee = error.response.data.email;
                console.clear();
            } else {
                console.log(error);
            }

        });
},

La función es casi idéntica a create pero con algunos cambios.

Se envía el id del empleado a modificar.

'id': this.idEmployee,

Se ha eliminado

error.response.data.email.forEach(function (element) {
       me.errorMessageEmployee.push(element);
});

Cambiandolo por simplemente

me.errorMessageEmployee = error.response.data.email;

Qué es exactamente lo mismo aunque si antes se utilizó forEach fue simplemente por mostrar una forma de uso.

Las validaciones son las mismas que create así como la política de errores.

La función del controlador quedaria asi:

public function update(UpdateEmployee $request)
{
    $today = Carbon::now();
    $unknow = Carbon::createFromFormat('d-m-Y', $request->birthday);
    if ($unknow->diffInYears($today) < 18) {
        return [
            'date' => ['El empleado tiene que tener mas de 18 años']
        ];
    } else {
        $employee = Employee::find($request->id);
        $employee->name = $request->name;
        $employee->lastname = $request->lastname;
        $employee->birthday = $unknow;
        $employee->email = $request->email;
        $employee->position_id = $request->position;
        $employee->save();
    }
}

El cambio más importante es el form request. Recordemos que hemos creado uno específico para la actualización: UpdateEmployee.

El resto de políticas de validaciones es igual que la crear y usamos find en lugar de new para modificar un empleado existente.

Llegados a este punto podemos decir que hemos terminado el CRUD prometido.

Código: Github

Unas aclaraciones finales:

  • El código es mejorable. Se prefirio poner mas codigo para hacer mas visual el funcionamiento pero se puede refactorizar de muchas maneras
  • El funcionamiento de todo el back es básico. Lo mínimo necesario para el funcionamiento del proyecto
  • Esta no es la única manera de hacer las cosas. Hay muchos caminos en programación para lograr un objetivo. Hemos elegido la mas simple y directa.
  • Hemos estado utilizando npm run dev pero para productos finales la forma correcta es npm run production

Por otro lado y llegados al final me gustaria saber si hay interes en continuar el proyecto hacia delante:

  • Mejorar validaciones (por ejemplo que no se puedan repetir cargos)
  • Crear componentes Vue relativos al proyecto
  • Convertir el proyecto en un SPA y el back en una API
  • Crear filtros, búsquedas y paginación en los listados del front
  • Trabajar más a fondo Bulma y elegir algun componente Vue de estilos como Vue-Material por ejemplo
  • Manejar un sistema de login y de permisos para visualizar informacion
  • Subir imagenes y mostrarla.
  • Otras ideas que se tengan
  • Hacer Test Unitarios
  • Mejorar el código a través de la refactorizacion.

Si se desea que la serie continúe en alguno de estos puntos estaremos encantados de saberlo en los comentarios.

Gracias por el interés mostrado hasta ahora.

Comparte este artículo

Entra en la discusión y deja tu comentario

Veces