Relaciones en Laravel

Establecer y definir correctamente las relaciones cuando construimos una APP, uno de los pasos más importantes para lograr una solución solida y escalable. Las relaciones entre modelos son la base de todo lo que haremos después.

Laravel aporta una estructura sencilla para construir sus modelos y relaciones, dentro del mundo de Eloquent, el cual aporta un conjunto de convenciones, que en esencia son estas

Convenciones de Eloquent Model

Los nombres de la tablas se escriben en minúsculas, corresponderán con el nombre del modelo y se le agrega una  s, y   si el modelo   tiene un nombre compuesto por dos palabras, la s se añade al final de todo, separando las palabras con una guion bajo.

De este modo , la tabla del modelo Cliente sería clientes y la de ClienteLike sería cliente_likes.
El campo primary key de una tabla debe tener el nombre de id, con el atributo AUTO_INCREMENT, en laravel 5 o bigIncrements en Laravel 6.

Los modelos son clases extienden de la clase Model, un modelo al ser creado tiene esta estructura

 <?php namespace App; use Illuminate\Database\Eloquent\Model; class NombreModelo extends Model {     // } 

Es aquí donde definiremos las relaciones y estructura del modelo, esto incluye la tabla a que se refiere, los campos que deberán ser llenados dentro de su tabla, y los cambios que queramos hacer en la estructura de la tabla que corresponde al modelo.

Las convenciones no son inviolables, sino una guía de organizar el código y su legibilidad pero pueden cambiarse todo lo que se desee dentro del modelo.  Por ejemplo un modelo Productos variado puede quedar asi

class Producto extends Model
 {
     // cambiamos el nombre de la tabla que controla el modelo en vez de productos, trabajara con mercancia
     protected $table = 'mercancia';
 // cambiamos la clave primaria, que por defecto es id pero usaremos el código ean definiendo ese campo en la propiedad  primaryKey protected $primaryKey = 'ean_code'; // Anulamos los valores por defecto de los campos  timestamp que Laravel crea automáticamente en cada tabla, cambiando su valor a false public $timestamps = false; // asignamos  un nombre de conexión diferente protected $connection = 'connection-name'; // Establecemos aquellos atributos  a los que se les pueden asignar valores protected $fillable = ['name',’fabricante’]; // Definimos en la variable guarded, los atributos que no pueden se asignados protected $guarded = ['price',’expire_date’];
 }
 <?php namespace App; use Illuminate\Database\Eloquent\Model; class Producto extends Model {     // cambiamos el nombre de la tabla que controla el modelo en vez de  productos, trabajara con mercancia     protected $table = 'mercancia';       // cambiamos la clave primaria, que por defecto es id pero usaremos  el código ean definiendo ese campo en la propiedad  primaryKey     protected $primaryKey = 'ean_code';       // Anulamos los valores por defecto de los campos  timestamp que  Laravel crea automáticamente en cada tabla, cambiando su valor a false     public $timestamps = false;       // asignamos  un nombre de conexión diferente     protected $connection = 'connection-name';       // Establecemos aquellos atributos  a los que se les pueden asignar  valores     protected $fillable = ['name',’fabricante’];       // Definimos en la variable guarded, los atributos que no pueden se  asignados     protected $guarded = ['price',’expire_date’]; } 

Las relaciones pueden ser de uno a uno, de uno  a muchos o de muchos a muchos, hay suficiente de esto en la red, bien explicado, pero en esencia en una aplicación donde un cliente, con un buzón de correos individual,  tendrá más de un rol, y habrá varios usuarios de la misma empresa, los modelos quedarían relacionados así.

Un cliente  con un buzón de correo (de uno a uno), pertenece solo a una empresa (de uno a uno) y un cliente tiene varios roles(relación de uno a muchos)

Como puede verse las relaciones son bilaterales, por tanto la inversa es que entre buzon y cliente también es de uno a uno, entre empresa    y clientes hay una relación de muchos a  uno  y entre roles y cliente también de uno a varios.

Cuando en ambos lados la relación es de uno a muchos,  se considera una relación de muchos a muchos y debe crearse una tabla pivote que acoja solo aquellos valores de ambos modelos que no son coincidentes y que será la tabla objeto de las consultas.

Si a las relaciones son de uno a muchos de  un lado y de uno a uno en el otro, es una relación de general de uno a muchos y si ambos lados son de uno a uno, pues asi queda de uno a uno.

Uno a Uno

Ejemplo Cliente y Buzón están relacionados uno a uno

namespace App;
use Illuminate\Database\Eloquent\Model;
class Cliente extends Model
{
public function buzon()
{
return $this->hasOne('App\Buzon');
// Si el id tiene un nombre diferente al que corresponde por convención buzons_id lo especificamos
return $this->hasOne('App\Buzon', 'code_id', 'local_key');
}
}

Definimos la otra clase

namespace App;
use Illuminate\Database\Eloquent\Model;
class Dni extends Model
{
public function cliente()
{
return $this->belongsTo('App\Cliente');
// Si el id tiene un nombre diferente al que corresponde por convención clientes_id lo especificamos
return $this->belongsTo('App\Cliente', 'nombre_id', 'local_key');
}
}

Uno a Muchos

En esta caso la definición es prácticamente igual

Una Empresa tiene muchos Clientes

namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function clientes()
{
return $this->hasMany('App\Cliente');
// Si el id tiene un nombre diferente al que corresponde por convención clientes_id lo especificamos
return $this->hasMany('App\Cliente', 'name_id', 'local_key'); }
}

Un Cliente pertenece a una Empresa

namespace App;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
public function empresa() { return $this->belongsTo('App\Empresa'); }
}

Muchos a Muchos

Un Cliente tiene varios Roles

namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
public function roles() { return $this->belongsToMany('App\Role');
// Si el nombre de la tabla es diferente a lo predeterminado o el ID de la tabla tiene otro nombre.
return $this->belongsToMany('App\Role', 'cliente_roles', 'name_id', 'defin_id');
}
}

Un Rol tiene varios Clientes

namespace App;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
public function clientes() { return $this->belongsToMany('App\Cliente');
// Si el nombre de la tabla es diferente a lo predeterminado o el ID de la tabla tiene otro nombre.
return $this->belongsToMany('App\User', 'cliente_roles', 'defin_id', 'name_id');
}
}

Aquí debemos crear una tabla intermedia, con el nombre cliente_roles, de las cual será donde obtengamos los datos. La idea básica es que esta tabla tendrá los id de las tablas que la conforman y otros campos que sean interesantes tener para nuestra aplicación

Definimos en la función withPivot los campos de la tabla intermedia, y el acceso a ellos.

namespace App;
use Illuminate\Database\Eloquent\Model;
class Cliente extends Model
{
public function roles()
{
return $this->belongsToMany('App\Role', 'cliente_roles')
->withPivot('create', 'read','update', 'delete');
}
}

Y para acceder  a los datos intermediamos la palabra pivot en la solicitud/orden  que hacemos. 

$user = App\Cliente::find(1);
foreach ($cliente->roles as $role) {
    echo $role->pivot->create;
}

Ademas podemos acceder directamente a las tablas para obtener diferentes resultados

// retorna App\Buzon
App\Cliente::first()->buzon;

Nos devolverá valores del modelo Buzon

// retorna Illuminate\Database\Eloquent\Relations\BelongsToMany
App\Cliente::first()->roles();

Nos devolverá valores del modelo Role, de acuerdo a su relación con el modelo cliente

// retorna Illuminate\Database\Eloquent\Collection
App\Cliente::first()->roles;
Nos devolverá una colección 

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

…., si avanzo sígueme, si me detengo empújame, si retrocedo mátame…….

Hacer una consulta con un subquery

$sub = Post::select(['id','titulo'])->where('estado','=',1);
$count = DB::table( DB::raw("({$sub->toSql()}) as sub") )
    ->mergeBindings($sub->getQuery()) 
    ->count();

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

Subir montañas hermana hombres……

Comandos de Artisan

logo laravel

La potencia  de Artisan para aquellos que ya entienden algo de Laravel, es indiscutible, al menos para mi y lo considero de gran ayuda.

Artisan posee un conjunto de comandos, que permiten que pueda aprovecharse muy bien su capacidad. Para listar estos comandos basta con escribir en la consola

  php artisan list 

e inmediatamente obtendremos:

Las opciones que disponibles con cada comando, pueden obtenerse mediante  help:

php artisan help nombre-comando 

o acortando simplemente con -h:

php artisan nombre-comando -h

Así obtendremos: la forma de usar el comando, sus argumentos , una descripción y las opciones que podemos agregar. Por ejemplo, para php artisan migrate -h  nos muestra:

Aquí nos está diciendo dice que “mígrate” es el comando para correr las migraciones y entre las opciones con que cuenta está poder indicar cuál es la base de datos por ejemplo php artisan migrate –database=tests .

Una de las grandes ventajas de esta herramienta es que tiene un conjunto de comandos dedicado a generadores, es decir, que nos permiten crear elementos como controladores, middleware, seeders, modelos, entre otros, los cuales son los que están bajo la categoría make:


Cada uno de esos generadores tiene sus propias opciones, así que puedes revisarlo con el comando help.

Artisan nos permite crear controladores sin métodos, mediante el uso de la opción plain:

php artisan make:controller PostController --plain

Abajo muestro una lista de algunos de los comandos de uso  más común.

Serve:

php artisan serve

Se utiliza para lanzar el servidor de una aplicación,  corriéndolo desde el root de nuestra app.

Este servidor viene incluido en laravel y se levanta de modo individual para cada  caso. Después de cargarlo, nos indicará donde está corriendo.   

laravel development server started on http://localhost:8000/

Si vamos al navegador, y visitamos  http://localhost:8000/,  veremos nuestra aplicación corriendo.

Para detener el servidor, utilizaremos la combinación de  las teclas Ctrl+C.  

Cambiar el namespace de la aplicación:

php artisan app:name nombre-namespace

Ver el listado completo de rutas: 

php artisan route:list


Como resultado nos mostrará un listado de todas las rutas de la aplicación, con la información sobre el método HTTP, la URI, la acción, el nombre y sus  middlewares.

Poner en modo mantenimiento nuestra aplicación:

php artisan down

Sacar la aplicacion del modo mantenimiento

php artisan up

Artisan además asimila crear comandos, lo cual es muy útil para ejecutar tareas manuales o testear comportamientos.

Crear una tabla

php artisan make:migration
create_posts_table --create posts 

Correr la migracion

php artisan migrate

Modificar una tabla existente

De este modo estariamos agregando el campo «resumen» a la tabla «articulos»

php artisan make:migration add_resumen_to_articulos_table --table="articulos"

Agregar varios campos nuevos a la tabla «users»

php artisan make:migration add_fields_to_users_table
--table="users"

Generar una llave para hashing

php artisan key:generate

Crear un middleware

Por ejemplo escribir esto para crear un middleware que verifique si el usuario tiene una sesión

php artisan make:middleware HasUserASession

Crear un modelo

El siguiente comando creara el modelo Tarea

php artisan make:model Tarea

Crear un controlador

Crear un controlador de recursos tipo REST (resource), que incluye todos los métodos del CRUD(create, read, update, delete)

php artisan make:controller TareaController --resource

Generar el código boilerplate para autenticación de usuarios

Este comando debería ejecutarse lo más pronto al iniciar tu proyecto ya que re-escribirá tus layouts y controladores de Autenticación y Usuarios

 php artisan make:auth

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

Subir montañas hermana hombres……

Como ponerle un alias a una tabla

Podemos tener la necesidad de crear un alias para una tabla, si nos vemos obligado a ello, podemos hacer esto.

$cars = Car::from('cars as c')
    ->join('marcas as m',function($join){
        $join->on('c.roles_id','=','m.id')->where('m.estado','=',1);
    })    
    ->select("c.*","m.name")
    ->where('c.estado','=',1)
    ->get();

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

…., si avanzo sígueme, si me detengo empújame, si retrocedo mátame…….

Ordenar las relaciones

En Laravel podemos ordenar las relaciones de acuerdo a diferentes parámetros, por el ejemplo size

class Category extends Model
{
	public function shoes()
	{
		return $this->hasMany('App\Shoe')->orderBy('size');
	}
}

Otro orden que podemos conseguir es al usar las tablas pivotes, para las relaciones belongsToMany.

En el siguiente ejemplo tenemos el modelo Role y el modelo User,  y estamos mostrando en una vista, la relación entre ambos de forma descendente, según la fecha en que fue creado, en grupos  de 15, por página.

class Role extends Model
{
	public function users()
	{
		return $this->belongsToMany('Role\User')->withTimestamps();
	}
}

@if ($roles->users->count() > 0)
	Asignación
	@foreach ($roles->users()->orderBy('rol_user.created_at', 'desc')->take(15)->get() as $user)
		...
	@endforeach
@endif

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

Subir montañas hermana a los hombres……

Crear prefijos a tabla y usarlas en consultas

Podemos  crear un  prefijo a todas las tablas de nuestra Base de Datos,  basta con ir al archivo  config/database.php, y en la opción prefix, añadir el que queremos usar

'mysql' => [
    'driver'    => 'mysql',
    'host'      => env('DB_HOST', 'localhost'),
    'database'  => env('DB_DATABASE', 'database'),
    'username'  => env('DB_USERNAME', 'root'),
    'password'  => env('DB_PASSWORD', ''),
    'charset'   => 'utf8',
    'collation' => 'utf8_unicode_ci',
    'prefix'    => 'prefijo_',
    'strict'    => false,
    'options' => array(),
],

En el caso de que realicemos consultas puras o  del tipo DB::raw tendremos que recordarle a Laravel el prefijo que usamos.
Primero  obtenemos el prefijo.

$db_prefix = DB::getTablePrefix();

Ahora dentro de la función DB::raw ingresamos el prefijo 

$productos = App\Producto::join("tipos","productos.tipos_id","=","tipos.id")
  ->leftJoin(DB::raw("(SELECT * FROM {$db_prefix}materials where {$db_prefix}materials.estado=1) as materials"),
    function($join){
      $join->on('productos.id','=','materials.productos_id');
    }
  )
  ->select(DB::raw("{$db_prefix}productos.*"))
  ->addSelect(DB::raw("DATE_FORMAT({$db_prefix}materials.created_at,'%d/%m/%Y %h:%i %p') AS materials_creado"))
  ->where('productos.estado','=',1)
  ->where('materials.pruebas_count','>',0)
  ->whereRaw(DB::raw("{$db_prefix}materials.etiquetas !=''"))
  ->get();
 $sql = "SELECT * FROM {$db_prefix}productos WHERE {$db_prefix}productos.estado = ? AND {$db_prefix}productos.fecha_caducidad < ?";
DB::select($sql,array(1,20));

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

….un sueño es casi todo  y más que  nada, más que todo al soñarlo, casi nada después……

Buscar una palabra en la base de datos

Un elemento común en una aplicación es que nos toque buscar palabras en una o mas columnas dentro de nuestra base de datos, aquí les muestro una forma de hacerlo que funciona.

Buscando una palabra en varias columnas o en todo un modelo

Lo que haremos es crear una variable query  y con el uso “use” y  de orWhere, nos compare el contenido de la columna con el valor de la búsqueda.

$busqueda = 'Palabra o termino a encontrar;
$descripcion = Descripcion::from('descripcion as a')
    ->where(function ($query) use ($busqueda) {
      $query = $query->orWhere('a.titulo','like',"%$busqueda%");
      $query = $query->orWhere('a.nombre','like',"%$busqueda%");
      $query = $query->orWhere('a.explicacion','like',"%$busqueda%");
      $query = $query->orWhere('a.etiquetas','like',"%$busqueda%");
    });
$posts = $posts->where('a.estado','=',1)
    ->get();

Buscando varias  palabras en varias columnas del modelo Usuario,  solo en aquellos  registros que están activos

$columnasbusqueda = ['nombre','apellidos','apodo'];
$terminos = 'Palabras a buscar';
$palabras = explode(" ",$ terminos);
$usuarios = Usuario::from('usuarios as a')
    ->where(function ($query) use ($columnasbusqueda,$ palabras) {
        foreach ($palabras as $palabra) {
            $query = $query->where(function ($query) use ($columnasbusqueda,$palabra) {
                foreach ($columnasbusqueda as $columna) {
                    $query->orWhere($columna,'like',"%$palabra%");
                }
            });
        }
    });
$usuarios = $usuarios->where('a.estado','=',1)
    ->get();

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

…..lo que realmente importa no es lo que te da la vida, sino lo que haces con ello…

Auth con el uso de modelos

logo laravel

Manejando algo en código podemos crear un sistema de autenticación mediante el uso de los modelos y sus relaciones, lo cual puede reforzar la seguridad de nuestra web.
En el ejemplo siguiente, creamos una función managePages(), dentro de la clase AdminPolicy, que nos indica una determinada condición para el usuario que intenta acceder, en este caso limita su uso solo a aquellos con los roles Admin y Editor.
Luego declaramos su validez dentro de Providers, específicamente en el constructor del AuthServiceProvider. Para esto lo que hacemos es pasarle la ruta exacta del GateContract y una variable que nos traerá los datos del user. Con el uso de un foreach le damos un valor dinamico al acceso.

// app/Policies/AdminPolicy.php
class AdminPolicy
{
public function managePages($user)
{
return $user->hasRole(['Admin', 'Editor']);
}
}
// app/Providers/AuthServiceProvider.php
public function boot( \Illuminate\Contracts\Auth\Access\GateContract $gate)
{
foreach (get_class_methods(new \App\Policies\AdminPolicy) as $method) {
$gate->define($method, \App\Policies\AdminPolicy::class . "@{$method}");
}
$this->registerPolicies($gate);
}

Luego en cada uno de los espacios donde necesitamos acceder, colocamos una clausula de acceso, que llamara a la clase que hemos creado

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

…………….subir montañas hermana hombres……..

Un error común al correr la migraciones:

Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table `users` add unique `users_email_unique`(`email`))

Dentro de App/Providers ir a AppServiceProvider y corregir la función boot

public function boot()
    {
        //
    }

Por

use Illuminate\Support\Facades\Schema; // añadir  facade Schema
public function boot()
    {
       // corrigiendo error …….
        Schema::defaultStringLength(191);
    }

Y listo, esto es todo.

Espero modestamente que este artículo, sirva de ayuda a alguien.

Gracias.

…..subir montañas hermana hombres…….