<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Laravel / PHP &#8211; Blog Maria Tech</title>
	<atom:link href="https://www.mariatech.com.mx/blog/categoria/laravel-php/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.mariatech.com.mx/blog</link>
	<description>Desarrollo web, tecnologías de la información y marketing digital</description>
	<lastBuildDate>Sun, 31 May 2026 23:41:15 +0000</lastBuildDate>
	<language>es</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://www.mariatech.com.mx/blog/wp-content/uploads/2024/05/cropped-icon_wordpress-32x32.png</url>
	<title>Laravel / PHP &#8211; Blog Maria Tech</title>
	<link>https://www.mariatech.com.mx/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Cómo cambiar la autenticación de GitHub de Access Token a SSH en un proyecto Laravel</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/como-cambiar-la-autenticacion-de-github-de-access-token-a-ssh-en-un-proyecto-laravel/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/como-cambiar-la-autenticacion-de-github-de-access-token-a-ssh-en-un-proyecto-laravel/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Sun, 31 May 2026 23:23:59 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<category><![CDATA[Git]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=278</guid>

					<description><![CDATA[En este artículo veremos cómo migrar la autenticación de un proyecto desde HTTPS con token hacia SSH, cómo validar que la conexión funcione...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Cuando trabajamos con proyectos alojados en GitHub, es común que al principio usemos una URL HTTPS con un <strong>Personal Access Token</strong> para clonar, descargar o actualizar el repositorio. Sin embargo, estos tokens pueden vencer, ser revocados o simplemente dejar de funcionar por cambios de seguridad.</p>



<p class="wp-block-paragraph">En este artículo veremos cómo migrar la autenticación de un proyecto desde HTTPS con token hacia SSH, cómo validar que la conexión funcione correctamente y qué hacer cuando Git no permite cambiar de rama porque existen archivos locales modificados, como puede pasar con archivos de configuración en Laravel.</p>



<h2 class="wp-block-heading">El problema: el token de acceso ya venció</h2>



<p class="wp-block-paragraph">Supongamos que tenemos un proyecto Laravel en un servidor de pruebas o demo. Al intentar ejecutar comandos como <code>git fetch</code>, <code>git pull</code> o <code>git checkout</code>, GitHub responde con un error de autenticación porque el token que se usaba anteriormente ya venció.</p>



<p class="wp-block-paragraph">En muchos casos, aunque ya hayamos creado una llave SSH en el servidor, Git seguirá usando el token anterior si el repositorio todavía tiene configurada una URL remota con HTTPS.</p>



<p class="wp-block-paragraph">Es decir, crear una llave SSH no cambia automáticamente la forma en que Git se conecta al repositorio. También debemos actualizar la URL remota del proyecto.</p>



<h2 class="wp-block-heading">1. Revisar la URL remota actual</h2>



<p class="wp-block-paragraph">Primero debemos entrar a la carpeta del proyecto y revisar qué URL remota está usando Git.</p>



<pre class="wp-block-code"><code>cd /ruta/general/del/proyecto

git remote -v</code></pre>



<p class="wp-block-paragraph">Si la respuesta muestra una URL similar a esta:</p>



<pre class="wp-block-code"><code>origin  https://github.com/usuario/repositorio.git (fetch)
origin  https://github.com/usuario/repositorio.git (push)</code></pre>



<p class="wp-block-paragraph">significa que el proyecto todavía está usando HTTPS.</p>



<p class="wp-block-paragraph">En algunos casos, incluso puede aparecer un token dentro de la URL, algo parecido a esto:</p>



<pre class="wp-block-code"><code>origin  https://TOKEN@github.com/usuario/repositorio.git</code></pre>



<p class="wp-block-paragraph">Ese es el escenario que debemos corregir.</p>



<h2 class="wp-block-heading">2. Cambiar la URL remota de HTTPS a SSH</h2>



<p class="wp-block-paragraph">Para que Git use la llave SSH, debemos cambiar la URL remota del repositorio usando el comando <code>git remote set-url</code>.</p>



<pre class="wp-block-code"><code>git remote set-url origin git@github.com:usuario/repositorio.git</code></pre>



<p class="wp-block-paragraph">Este comando cambia el origen del repositorio para que Git deje de usar HTTPS y empiece a usar SSH.</p>



<p class="wp-block-paragraph">Después podemos verificar nuevamente la configuración:</p>



<pre class="wp-block-code"><code>git remote -v</code></pre>



<p class="wp-block-paragraph">La salida esperada sería algo como:</p>



<pre class="wp-block-code"><code>origin  git@github.com:usuario/repositorio.git (fetch)
origin  git@github.com:usuario/repositorio.git (push)</code></pre>



<h2 class="wp-block-heading">3. Probar la conexión SSH con GitHub</h2>



<p class="wp-block-paragraph">Antes de intentar descargar ramas o actualizar el proyecto, conviene probar que la llave SSH realmente esté funcionando.</p>



<pre class="wp-block-code"><code>ssh -T git@github.com</code></pre>



<p class="wp-block-paragraph">Si la autenticación es correcta, GitHub responderá con un mensaje indicando que la conexión fue exitosa, aunque no permita acceso de shell. Esto es normal.</p>



<p class="wp-block-paragraph">Una respuesta esperada puede ser similar a:</p>



<pre class="wp-block-code"><code>Hi usuario! You've successfully authenticated, but GitHub does not provide shell access.</code></pre>



<p class="wp-block-paragraph">Con eso confirmamos que la autenticación SSH ya está funcionando.</p>



<h2 class="wp-block-heading">4. Descargar información actualizada del repositorio</h2>



<p class="wp-block-paragraph">Una vez configurada la URL SSH, podemos ejecutar:</p>



<pre class="wp-block-preformatted">git fetch origin</pre>



<p class="wp-block-paragraph">Este comando descarga la información actualizada de las ramas remotas sin mezclar cambios todavía en la rama actual.</p>



<h2 class="wp-block-heading">5. Cambiar a una rama remota</h2>



<p class="wp-block-paragraph">Si necesitamos crear una rama local a partir de una rama remota, podemos usar:</p>



<pre class="wp-block-code"><code>git checkout -b nombre-rama origin/nombre-rama</code></pre>



<p class="wp-block-paragraph">Sin embargo, en proyectos reales puede aparecer un error indicando que un archivo local tiene cambios y que Git necesita sobrescribirlo para cambiar de rama.</p>



<p class="wp-block-paragraph">Un caso común en Laravel puede ser un archivo como:</p>



<pre class="wp-block-code"><code>config/app.php</code></pre>



<h2 class="wp-block-heading">6. ¿Por qué pasa este error?</h2>



<p class="wp-block-paragraph">Git protege los cambios locales para evitar que se pierdan accidentalmente. Si tenemos modificaciones en un archivo y la rama a la que queremos cambiar también modifica ese archivo, Git detiene el proceso.</p>



<p class="wp-block-paragraph">A veces se intenta usar el comando <code>assume-unchanged</code> para que Git ignore temporalmente un archivo:</p>



<pre class="wp-block-code"><code>git update-index --assume-unchanged config/app.php</code></pre>



<p class="wp-block-paragraph">Este comando puede ayudar en algunos escenarios locales, pero no es una solución ideal para manejar archivos de configuración modificados en servidores o ambientes de demo.</p>



<p class="wp-block-paragraph">Además, aunque un archivo esté marcado como <code>assume-unchanged</code>, Git aún puede bloquear operaciones si necesita proteger cambios locales antes de cambiar de rama.</p>



<h2 class="wp-block-heading">7. Revisar si un archivo está marcado como assume-unchanged</h2>



<p class="wp-block-paragraph">Podemos revisar el estado del archivo con:</p>



<pre class="wp-block-code"><code>git ls-files -v config/app.php</code></pre>



<p class="wp-block-paragraph">Si el archivo aparece con una letra minúscula al inicio, significa que está marcado como <code>assume-unchanged</code>.</p>



<h2 class="wp-block-heading">8. Quitar assume-unchanged</h2>



<p class="wp-block-paragraph">Antes de resolver el conflicto, es recomendable quitar esa marca para que Git vuelva a tratar el archivo normalmente.</p>



<pre class="wp-block-code"><code>git update-index --no-assume-unchanged config/app.php</code></pre>



<h2 class="wp-block-heading">9. Opción segura: guardar temporalmente los cambios con stash</h2>



<p class="wp-block-paragraph">Si necesitamos conservar los cambios locales del archivo, podemos guardarlos temporalmente con <code>git stash</code>.</p>



<pre class="wp-block-code"><code>git stash push -m "Backup config app" -- config/app.php</code></pre>



<p class="wp-block-paragraph">Después ya podemos intentar cambiar de rama:</p>



<pre class="wp-block-code"><code>git checkout -b nombre-rama origin/nombre-rama</code></pre>



<p class="wp-block-paragraph">Y si necesitamos recuperar los cambios guardados:</p>



<pre class="wp-block-code"><code>git stash pop</code></pre>



<p class="wp-block-paragraph">Si al recuperar los cambios aparecen conflictos, deberán resolverse manualmente.</p>



<h2 class="wp-block-heading">10. Opción rápida: descartar cambios locales</h2>



<p class="wp-block-paragraph">Si los cambios locales no son importantes, podemos descartarlos.</p>



<pre class="wp-block-code"><code>git restore config/app.php</code></pre>



<p class="wp-block-paragraph">Después intentamos nuevamente cambiar de rama:</p>



<pre class="wp-block-code"><code>git checkout -b nombre-rama origin/nombre-rama</code></pre>



<p class="wp-block-paragraph">Esta opción debe usarse con cuidado, porque elimina los cambios locales del archivo.</p>



<h2 class="wp-block-heading">11. Mejor práctica en Laravel: usar .env para configuración por ambiente</h2>



<p class="wp-block-paragraph">En Laravel, lo más recomendable es evitar modificar archivos como <code>config/app.php</code> directamente en cada servidor. Si una configuración cambia dependiendo del ambiente, lo ideal es manejarla desde el archivo <code>.env</code>.</p>



<p class="wp-block-paragraph">Por ejemplo, en lugar de dejar un valor fijo dentro de <code>config/app.php</code>, podemos leerlo desde una variable de entorno:</p>



<pre class="wp-block-code"><code>'custom_product' =&gt; env('APP_CUSTOM_PRODUCT', false),</code></pre>



<p class="wp-block-paragraph">Y en el archivo <code>.env</code> del servidor:</p>



<pre class="wp-block-code"><code>APP_CUSTOM_PRODUCT=true</code></pre>



<p class="wp-block-paragraph">De esta manera cada ambiente puede tener su propia configuración sin modificar archivos versionados en Git.</p>



<h2 class="wp-block-heading">12. Flujo recomendado completo</h2>



<p class="wp-block-paragraph">Un flujo seguro para este tipo de situación sería:</p>



<pre class="wp-block-code"><code>cd /ruta/general/del/proyecto

git remote -v

git remote set-url origin git@github.com:usuario/repositorio.git

git remote -v

ssh -T git@github.com

git fetch origin

git update-index --no-assume-unchanged config/app.php

git stash push -m "Backup config app" -- config/app.php

git checkout -b nombre-rama origin/nombre-rama</code></pre>



<p class="wp-block-paragraph">Si después necesitamos recuperar los cambios locales:</p>



<pre class="wp-block-code"><code>git stash pop</code></pre>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">Cuando un proyecto deja de autenticarse con GitHub porque venció un access token, una buena solución es migrar la conexión del repositorio a SSH. Para que esto funcione, no basta con crear la llave SSH: también debemos cambiar la URL remota del repositorio.</p>



<p class="wp-block-paragraph">Además, si Git bloquea un cambio de rama por archivos modificados, debemos decidir si queremos conservar esos cambios con <code>git stash</code> o descartarlos con <code>git restore</code>.</p>



<p class="wp-block-paragraph">En proyectos Laravel, la recomendación más limpia es mantener los archivos de configuración versionados sin modificaciones locales y mover los valores específicos de cada ambiente al archivo <code>.env</code>. Esto evita conflictos, mejora el flujo de despliegue y reduce errores al trabajar con ramas en Git.</p>



<p class="wp-block-paragraph">Si necesitas ayuda para crear un proyecto de software en <strong>Maria Tech </strong>contamos con los expertos para apoyarte.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://wa.me/523414305984" target="_blank" rel="noreferrer noopener">(+52) 341-430-59-84</a></div>
</div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="mailto:info@mariatech.com.mx" target="_blank" rel="noreferrer noopener">info@mariatech.com.mx</a></div>
</div>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/como-cambiar-la-autenticacion-de-github-de-access-token-a-ssh-en-un-proyecto-laravel/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cómo usar Form Request Validation para formularios enviados con Ajax</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/como-usar-form-request-validation-para-formularios-enviados-con-ajax/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/como-usar-form-request-validation-para-formularios-enviados-con-ajax/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Sun, 31 May 2026 04:38:25 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=275</guid>

					<description><![CDATA[En este artículo veremos una forma práctica de trabajar con Request validation con formularios Ajax...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Cuando trabajamos con formularios en Laravel, lo más común es validar la información usando <strong>Form Requests</strong>. Esto permite mantener los controladores limpios y centralizar las reglas de validación en clases independientes.</p>



<p class="wp-block-paragraph">Sin embargo, cuando el formulario se envía por medio de <strong>Ajax</strong>, especialmente desde modales o componentes dinámicos, surge una duda común: ¿cómo devolvemos los errores de validación en formato JSON sin dejar de usar la estructura nativa de Laravel?</p>



<p class="wp-block-paragraph">En este artículo veremos una forma práctica de trabajar con <strong>Request validation</strong> con formularios Ajax, respuestas JSON y códigos HTTP correctos.</p>



<h2 class="wp-block-heading">El problema común</h2>



<p class="wp-block-paragraph">En un formulario tradicional, si la validación falla, Laravel redirige automáticamente al usuario a la página anterior y muestra los errores en la vista.</p>



<p class="wp-block-paragraph">Pero cuando usamos Ajax, no queremos una redirección. Queremos una respuesta JSON que podamos procesar desde JavaScript para mostrar los errores dentro de un modal, un toast o una alerta.</p>



<p class="wp-block-paragraph">Una solución rápida suele ser hacer la validación directamente en el controlador:</p>



<pre class="wp-block-code"><code>$validator = Validator::make($request-&gt;all(), $rules, $messages);

if ($validator-&gt;fails()) {
    return response()-&gt;json(&#91;
        'success' =&gt; false,
        'errors' =&gt; $validator-&gt;errors()-&gt;all(),
    ], 422);
}</code></pre>



<p class="wp-block-paragraph">Aunque esto funciona, tiene una desventaja importante: la validación queda mezclada dentro del controlador. Conforme el proyecto crece, esto vuelve el código más difícil de mantener.</p>



<h2 class="wp-block-heading">La mejor opción: seguir usando Form Request</h2>



<p class="wp-block-paragraph">Laravel permite seguir usando Form Requests aun cuando el formulario se envía por Ajax. Lo único que necesitamos es personalizar la respuesta cuando la validación falla.</p>



<p class="wp-block-paragraph">Primero, podemos crear un Form Request:</p>



<pre class="wp-block-code"><code>php artisan make:request Admin/User/StoreUserRequest</code></pre>



<p class="wp-block-paragraph">Luego definimos las reglas de validación:</p>



<pre class="wp-block-code"><code>namespace App\Http\Requests\Admin\User;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

class StoreUserRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return &#91;
            'name' => &#91;'required', 'string', 'max:255'],
            'last_name' => &#91;'required', 'string', 'max:255'],
            'email' => &#91;'required', 'email', 'max:255', Rule::unique('users', 'email')],
            'phone' => &#91;'nullable', 'digits:10'],
            'id_profile_type' => &#91;'required', 'exists:profile_types,id'],
            'password' => &#91;'required', 'string', 'min:8', 'confirmed'],
            'enabled' => &#91;'nullable', 'boolean'],
        ];
    }
}</code></pre>



<h2 class="wp-block-heading">Personalizar la respuesta JSON de validación</h2>



<p class="wp-block-paragraph">Por defecto, Laravel devuelve los errores agrupados por campo:</p>



<pre class="wp-block-code"><code>{
    "message": "The given data was invalid.",
    "errors": {
        "email": &#91;
            "Este correo electrónico ya está registrado."
        ],
        "password": &#91;
            "La contraseña debe tener al menos 8 caracteres."
        ]
    }
}</code></pre>



<p class="wp-block-paragraph">Este formato es útil cuando queremos marcar errores campo por campo. Pero si nuestra interfaz usa modales y queremos mostrar una lista general de errores, puede ser más cómodo devolver una lista plana.</p>



<p class="wp-block-paragraph">Para eso podemos sobrescribir el método <code>failedValidation</code> dentro del StoreUserRequest:</p>



<pre class="wp-block-code"><code>use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;

protected function failedValidation(Validator $validator): void
{
    throw new HttpResponseException(response()-&gt;json(&#91;
        'success' =&gt; false,
        'message' =&gt; 'Hay errores en el formulario.',
        'errors' =&gt; $validator-&gt;errors()-&gt;all(),
    ], 422));
}</code></pre>



<p class="wp-block-paragraph">Con esto, la respuesta quedará así:</p>



<pre class="wp-block-code"><code>{
    "success": false,
    "message": "Hay errores en el formulario.",
    "errors": &#91;
        "El nombre es obligatorio.",
        "Este correo electrónico ya está registrado.",
        "La contraseña debe tener al menos 8 caracteres."
    ]
}</code></pre>



<h2 class="wp-block-heading">Controlador limpio usando el Form Request</h2>



<p class="wp-block-paragraph">Una vez que el Form Request está listo, el controlador puede mantenerse limpio:</p>



<pre class="wp-block-code"><code>use App\Http\Requests\Admin\User\StoreUserRequest;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Hash;
use Throwable;

public function store(StoreUserRequest $request): JsonResponse
{
    try {
        $validated = $request-&gt;validated();

        $user = User::create(&#91;
            'name' =&gt; $validated&#91;'name'],
            'last_name' =&gt; $validated&#91;'last_name'],
            'email' =&gt; $validated&#91;'email'],
            'phone' =&gt; $validated&#91;'phone'] ?? null,
            'id_profile_type' =&gt; $validated&#91;'id_profile_type'],
            'password' =&gt; Hash::make($validated&#91;'password']),
            'enabled' =&gt; $validated&#91;'enabled'] ?? 1,
        ]);

        return response()-&gt;json(&#91;
            'success' =&gt; true,
            'message' =&gt; 'Usuario registrado correctamente.',
            'user_id' =&gt; $user-&gt;id,
        ], 201);

    } catch (Throwable $e) {
        report($e);

        return response()-&gt;json(&#91;
            'success' =&gt; false,
            'message' =&gt; 'No fue posible registrar el usuario. Inténtalo nuevamente.',
        ], 500);
    }
}</code></pre>



<h2 class="wp-block-heading">Códigos HTTP recomendados</h2>



<p class="wp-block-paragraph">Es importante regresar códigos HTTP correctos para que el frontend pueda interpretar bien la respuesta.</p>



<ul class="wp-block-list">
<li><strong>200:</strong> operación correcta.</li>



<li><strong>201:</strong> registro creado correctamente.</li>



<li><strong>401:</strong> usuario no autenticado.</li>



<li><strong>403:</strong> usuario autenticado, pero sin permisos.</li>



<li><strong>404:</strong> recurso no encontrado.</li>



<li><strong>422:</strong> error de validación.</li>



<li><strong>500:</strong> error inesperado del servidor.</li>
</ul>



<p class="wp-block-paragraph">Para formularios Ajax, una convención práctica sería:</p>



<pre class="wp-block-code"><code>Validación fallida  → 422
Creación correcta   → 201
Actualización       → 200
Eliminación         → 200
No encontrado       → 404
Sin permisos        → 403
Error inesperado    → 500</code></pre>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">Usar Form Requests con formularios Ajax es una forma limpia y profesional de validar datos en Laravel sin sacrificar la experiencia de usuario.</p>



<p class="wp-block-paragraph">La clave está en no llevar las reglas de validación al controlador, sino mantenerlas dentro del Request y personalizar la respuesta JSON cuando la validación falla.</p>



<p class="wp-block-paragraph">Con esta estructura logramos:</p>



<ul class="wp-block-list">
<li>Controladores más limpios.</li>



<li>Validaciones reutilizables.</li>



<li>Respuestas JSON consistentes.</li>



<li>Códigos HTTP correctos.</li>



<li>Mejor integración con formularios en modales.</li>



<li>Mejor experiencia de usuario usando Ajax.</li>
</ul>



<p class="wp-block-paragraph">Esta forma de trabajo es especialmente útil en paneles administrativos, sistemas internos, CRMs, ERPs y plataformas donde los formularios se muestran en modales o secciones dinámicas.</p>



<p class="wp-block-paragraph">Si necesitas ayuda para crear un proyecto de software en <strong>Maria Tech </strong>contamos con los expertos para apoyarte.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://wa.me/523414305984" target="_blank" rel="noreferrer noopener">(+52) 341-430-59-84</a></div>
</div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="mailto:info@mariatech.com.mx" target="_blank" rel="noreferrer noopener">info@mariatech.com.mx</a></div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/como-usar-form-request-validation-para-formularios-enviados-con-ajax/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cómo implementar recuperación de contraseña para administradores y clientes en Laravel 13</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-recuperacion-de-contrasena-para-administradores-y-clientes-en-laravel-13/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-recuperacion-de-contrasena-para-administradores-y-clientes-en-laravel-13/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Sun, 24 May 2026 02:56:48 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=267</guid>

					<description><![CDATA[En este artículo veremos cómo estructurar un proceso de recuperación de contraseña en Laravel para dos tipos de usuarios: administradores y clientes.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">En proyectos web con áreas separadas para administradores y clientes, es común que ambos tipos de usuarios necesiten iniciar sesión, cerrar sesión y recuperar su contraseña. El reto técnico aparece cuando cada tipo de usuario vive en una tabla diferente y debe tener su propio flujo de autenticación.</p>



<p class="wp-block-paragraph">En este artículo veremos cómo estructurar un proceso de recuperación de contraseña en Laravel 13 para dos tipos de usuarios: <strong>administradores</strong> y <strong>clientes</strong>. El objetivo es mantener una arquitectura limpia, segura y fácil de mantener.</p>



<h2 class="wp-block-heading">Contexto del problema</h2>



<p class="wp-block-paragraph">Laravel Breeze instala un sistema de autenticación funcional, pero por defecto está pensado para trabajar principalmente con una sola tabla de usuarios, normalmente <code>users</code>.</p>



<p class="wp-block-paragraph">Sin embargo, en proyectos más completos podemos necesitar algo como esto:</p>



<ul class="wp-block-list">
<li><strong>Administradores:</strong> usuarios internos del sistema, almacenados en la tabla <code>users</code>.</li>



<li><strong>Clientes:</strong> usuarios externos que acceden al sitio o portal del cliente, almacenados en la tabla <code>customers</code>.</li>
</ul>



<p class="wp-block-paragraph">En este escenario no conviene mezclar ambos tipos de usuario en la misma tabla, porque normalmente tienen permisos, vistas, reglas de negocio y flujos diferentes.</p>



<h2 class="wp-block-heading">Estructura recomendada</h2>



<pre class="wp-block-code"><code>app/
├── Http/
│   ├── Controllers/
│   │   ├── Admin/
│   │   │   └── Auth/
│   │   │       ├── PasswordResetLinkController.php
│   │   │       └── NewPasswordController.php
│   │   └── Customer/
│   │       └── Auth/
│   │           ├── PasswordResetLinkController.php
│   │           └── NewPasswordController.php

resources/
├── views/
│   ├── admin/
│   │   └── auth/
│   │       ├── forgot-password.blade.php
│   │       └── reset-password.blade.php
│   └── customer/
│       └── auth/
│           ├── forgot-password.blade.php
│           └── reset-password.blade.php

routes/
├── admin.php
└── customer.php</code></pre>



<p class="wp-block-paragraph">La idea es separar cada flujo para evitar dependencias innecesarias y tener rutas, controladores, vistas y brokers independientes.</p>



<h2 class="wp-block-heading">Configuración de autenticación</h2>



<p class="wp-block-paragraph">En <code>config/auth.php</code> podemos definir un guard y un provider para cada tipo de usuario.</p>



<pre class="wp-block-code"><code>&lt;?php

use App\Models\User;
use App\Models\Customer;

return &#91;

    'defaults' =&gt; &#91;
        'guard' =&gt; 'web',
        'passwords' =&gt; 'users',
    ],

    'guards' =&gt; &#91;
        'web' =&gt; &#91;
            'driver' =&gt; 'session',
            'provider' =&gt; 'users',
        ],

        'customer' =&gt; &#91;
            'driver' =&gt; 'session',
            'provider' =&gt; 'customers',
        ],
    ],

    'providers' =&gt; &#91;
        'users' =&gt; &#91;
            'driver' =&gt; 'eloquent',
            'model' =&gt; User::class,
        ],

        'customers' =&gt; &#91;
            'driver' =&gt; 'eloquent',
            'model' =&gt; Customer::class,
        ],
    ],

    'passwords' =&gt; &#91;
        'users' =&gt; &#91;
            'provider' =&gt; 'users',
            'table' =&gt; 'password_reset_tokens',
            'expire' =&gt; 60,
            'throttle' =&gt; 60,
        ],

        'customers' =&gt; &#91;
            'provider' =&gt; 'customers',
            'table' =&gt; 'customer_password_reset_tokens',
            'expire' =&gt; 60,
            'throttle' =&gt; 60,
        ],
    ],

];</code></pre>



<p class="wp-block-paragraph">El punto clave está en la sección <code>passwords</code>. Ahí definimos dos brokers: <code>users</code> para administradores y <code>customers</code> para clientes. Cada broker usa su propio provider y su propia tabla de tokens.</p>



<h2 class="wp-block-heading">Migración para tokens de clientes</h2>



<pre class="wp-block-code"><code>Schema::create('customer_password_reset_tokens', function (Blueprint $table) {
    $table-&gt;string('email')-&gt;primary();
    $table-&gt;string('token');
    $table-&gt;timestamp('created_at')-&gt;nullable();
});</code></pre>



<p class="wp-block-paragraph">Esto permite que el flujo de administradores y clientes quede completamente separado.</p>



<h2 class="wp-block-heading">Rutas para administradores</h2>



<pre class="wp-block-code"><code>use App\Http\Controllers\Admin\Auth\PasswordResetLinkController;
use App\Http\Controllers\Admin\Auth\NewPasswordController;
use Illuminate\Support\Facades\Route;

Route::middleware('admin.guest')->group(function () {
    Route::get('/forgot-password', &#91;PasswordResetLinkController::class, 'create'])
        ->name('password.request');

    Route::post('/forgot-password', &#91;PasswordResetLinkController::class, 'store'])
        ->name('password.email');

    Route::get('/reset-password/{token}', &#91;NewPasswordController::class, 'create'])
        ->name('password.reset');

    Route::post('/reset-password', &#91;NewPasswordController::class, 'store'])
        ->name('password.store');
});</code></pre>



<h2 class="wp-block-heading">Rutas para clientes</h2>



<pre class="wp-block-code"><code>use App\Http\Controllers\Customer\Auth\PasswordResetLinkController;
use App\Http\Controllers\Customer\Auth\NewPasswordController;
use Illuminate\Support\Facades\Route;

Route::middleware('customer.guest')->group(function () {
    Route::get('/forgot-password', &#91;PasswordResetLinkController::class, 'create'])
        ->name('password.request');

    Route::post('/forgot-password', &#91;PasswordResetLinkController::class, 'store'])
        ->name('password.email');

    Route::get('/reset-password/{token}', &#91;NewPasswordController::class, 'create'])
        ->name('password.reset');

    Route::post('/reset-password', &#91;NewPasswordController::class, 'store'])
        ->name('password.store');
});</code></pre>



<h2 class="wp-block-heading">Controlador para enviar el enlace de recuperación</h2>



<p class="wp-block-paragraph">El controlador encargado de enviar el enlace de recuperación debe usar el broker correcto.</p>



<h3 class="wp-block-heading">Administrador</h3>



<pre class="wp-block-code"><code>namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;

class PasswordResetLinkController extends Controller
{
    public function create()
    {
        return view('admin.auth.forgot-password');
    }

    public function store(Request $request)
    {
        $request-&gt;validate(&#91;
            'email' =&gt; &#91;'required', 'email'],
        ]);

        $status = Password::broker('users')-&gt;sendResetLink(
            $request-&gt;only('email')
        );

        if ($status === Password::RESET_LINK_SENT) {
            return back()-&gt;with('status', __($status));
        }

        throw ValidationException::withMessages(&#91;
            'email' =&gt; __($status),
        ]);
    }
}</code></pre>



<h3 class="wp-block-heading">Cliente</h3>



<pre class="wp-block-code"><code>namespace App\Http\Controllers\Customer\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
use Illuminate\Validation\ValidationException;

class PasswordResetLinkController extends Controller
{
    public function create()
    {
        return view('customer.auth.forgot-password');
    }

    public function store(Request $request)
    {
        $request-&gt;validate(&#91;
            'email' =&gt; &#91;'required', 'email'],
        ]);

        $status = Password::broker('customers')-&gt;sendResetLink(
            $request-&gt;only('email')
        );

        if ($status === Password::RESET_LINK_SENT) {
            return back()-&gt;with('status', __($status));
        }

        throw ValidationException::withMessages(&#91;
            'email' =&gt; __($status),
        ]);
    }
}</code></pre>



<p class="wp-block-paragraph">El cambio central es el broker:</p>



<pre class="wp-block-code"><code>Password::broker('users')      // Administradores
Password::broker('customers')  // Clientes</code></pre>



<h2 class="wp-block-heading">Controlador para actualizar la contraseña</h2>



<h3 class="wp-block-heading">Administrador</h3>



<pre class="wp-block-code"><code>namespace App\Http\Controllers\Admin\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;

class NewPasswordController extends Controller
{
    public function create(Request $request)
    {
        return view('admin.auth.reset-password', &#91;
            'request' =&gt; $request,
        ]);
    }

    public function store(Request $request)
    {
        $request-&gt;validate(&#91;
            'token' =&gt; &#91;'required'],
            'email' =&gt; &#91;'required', 'email'],
            'password' =&gt; &#91;'required', 'confirmed', Rules\Password::defaults()],
        ]);

        $status = Password::broker('users')-&gt;reset(
            $request-&gt;only('email', 'password', 'password_confirmation', 'token'),
            function ($user) use ($request) {
                $user-&gt;forceFill(&#91;
                    'password' =&gt; Hash::make($request-&gt;password),
                    'remember_token' =&gt; Str::random(60),
                ])-&gt;save();

                event(new PasswordReset($user));
            }
        );

        return $status === Password::PASSWORD_RESET
            ? redirect()-&gt;route('admin.login')-&gt;with('status', __($status))
            : back()-&gt;withInput($request-&gt;only('email'))
                -&gt;withErrors(&#91;'email' =&gt; __($status)]);
    }
}</code></pre>



<h3 class="wp-block-heading">Cliente</h3>



<pre class="wp-block-code"><code>namespace App\Http\Controllers\Customer\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules;

class NewPasswordController extends Controller
{
    public function create(Request $request)
    {
        return view('customer.auth.reset-password', &#91;
            'request' =&gt; $request,
        ]);
    }

    public function store(Request $request)
    {
        $request-&gt;validate(&#91;
            'token' =&gt; &#91;'required'],
            'email' =&gt; &#91;'required', 'email'],
            'password' =&gt; &#91;'required', 'confirmed', Rules\Password::defaults()],
        ]);

        $status = Password::broker('customers')-&gt;reset(
            $request-&gt;only('email', 'password', 'password_confirmation', 'token'),
            function ($customer) use ($request) {
                $customer-&gt;forceFill(&#91;
                    'password' =&gt; Hash::make($request-&gt;password),
                    'remember_token' =&gt; Str::random(60),
                ])-&gt;save();

                event(new PasswordReset($customer));
            }
        );

        return $status === Password::PASSWORD_RESET
            ? redirect()-&gt;route('customer.login')-&gt;with('status', __($status))
            : back()-&gt;withInput($request-&gt;only('email'))
                -&gt;withErrors(&#91;'email' =&gt; __($status)]);
    }
}</code></pre>



<h2 class="wp-block-heading">Personalizar la URL del correo de recuperación</h2>



<p class="wp-block-paragraph">Un detalle importante es que Laravel genera el enlace de recuperación usando una notificación. Si no personalizamos ese enlace, puede terminar enviando al usuario al flujo equivocado.</p>



<p class="wp-block-paragraph">Para resolverlo, podemos agregar la lógica en <code>app/Providers/AppServiceProvider.php</code>.</p>



<pre class="wp-block-code"><code>namespace App\Providers;

use App\Models\Customer;
use App\Models\User;
use Illuminate\Auth\Notifications\ResetPassword;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        //
    }

    public function boot(): void
    {
        ResetPassword::createUrlUsing(function (object $notifiable, string $token): string {
            if ($notifiable instanceof Customer) {
                return URL::route('customer.password.reset', &#91;
                    'token' =&gt; $token,
                    'email' =&gt; $notifiable-&gt;getEmailForPasswordReset(),
                ]);
            }

            if ($notifiable instanceof User) {
                return URL::route('admin.password.reset', &#91;
                    'token' =&gt; $token,
                    'email' =&gt; $notifiable-&gt;getEmailForPasswordReset(),
                ]);
            }

            return URL::route('customer.password.reset', &#91;
                'token' =&gt; $token,
                'email' =&gt; $notifiable-&gt;getEmailForPasswordReset(),
            ]);
        });
    }
}</code></pre>



<p class="wp-block-paragraph">Con esto, cuando el modelo sea <code>User</code>, Laravel enviará al flujo de administrador. Cuando el modelo sea <code>Customer</code>, enviará al flujo de cliente.</p>



<h2 class="wp-block-heading">Vistas de recuperación de contraseña</h2>



<p class="wp-block-paragraph">En cada formulario es importante apuntar a la ruta correcta.</p>



<h3 class="wp-block-heading">Formulario para solicitar enlace</h3>



<p class="wp-block-paragraph">Administrador:</p>



<pre class="wp-block-code"><code>&lt;form method="POST" action="{{ route('admin.password.email') }}">
    @csrf
    &lt;input type="email" name="email" required>
    &lt;button type="submit">
        Enviar enlace
    &lt;/button>
&lt;/form></code></pre>



<p class="wp-block-paragraph">Cliente:</p>



<pre class="wp-block-code"><code>&lt;form method="POST" action="{{ route('customer.password.email') }}">
    @csrf
    &lt;input type="email" name="email" required>
    &lt;button type="submit">
        Enviar enlace
    &lt;/button>
&lt;/form></code></pre>



<h3 class="wp-block-heading">Formulario para crear nueva contraseña</h3>



<p class="wp-block-paragraph">Administrador:</p>



<pre class="wp-block-code"><code>&lt;form method="POST" action="{{ route('admin.password.store') }}">
    @csrf
    &lt;input type="hidden" name="token" value="{{ $request->route('token') }}">
    &lt;input type="email" name="email" value="{{ old('email', $request->email) }}" required>
    &lt;input type="password" name="password" required>
    &lt;input type="password" name="password_confirmation" required>
    &lt;button type="submit">
        Actualizar contraseña
    &lt;/button>
&lt;/form></code></pre>



<p class="wp-block-paragraph">Cliente:</p>



<pre class="wp-block-code"><code>&lt;form method="POST" action="{{ route('customer.password.store') }}">
    @csrf
    &lt;input type="hidden" name="token" value="{{ $request->route('token') }}">
    &lt;input type="email" name="email" value="{{ old('email', $request->email) }}" required>
    &lt;input type="password" name="password" required>
    &lt;input type="password" name="password_confirmation" required>
    &lt;button type="submit">
        Actualizar contraseña
    &lt;/button>
&lt;/form></code></pre>



<h2 class="wp-block-heading">Buenas prácticas</h2>



<ul class="wp-block-list">
<li>Usar modelos separados cuando los usuarios internos y externos tienen responsabilidades diferentes.</li>



<li>Separar rutas, vistas y controladores por tipo de usuario.</li>



<li>Usar un broker de contraseña diferente para cada tabla.</li>



<li>Personalizar la URL del correo para evitar que el usuario llegue al flujo equivocado.</li>



<li>No depender completamente de la estructura por defecto de Breeze cuando el proyecto requiere múltiples áreas de autenticación.</li>



<li>Mantener nombres claros como <code>Admin</code> y <code>Customer</code> para facilitar el mantenimiento.</li>
</ul>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">La recuperación de contraseña para múltiples tipos de usuarios en Laravel requiere separar correctamente guards, providers, brokers, rutas, controladores y vistas. Aunque Breeze ofrece una buena base inicial, en proyectos con administradores y clientes conviene tomar el control del flujo para evitar cruces entre autenticaciones.</p>



<p class="wp-block-paragraph">La clave está en usar un broker distinto para cada tipo de usuario:</p>



<pre class="wp-block-code"><code>Password::broker('users')      // Administradores
Password::broker('customers')  // Clientes</code></pre>



<p class="wp-block-paragraph">Y personalizar el enlace generado por correo desde <code>AppServiceProvider</code>, para que cada usuario llegue al formulario correcto.</p>



<p class="wp-block-paragraph">Con esta estructura, el sistema queda más limpio, seguro y preparado para crecer.</p>



<p class="wp-block-paragraph">¡Si te ha servido de algo este artículo no olvides dejarnos un comentario!</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-recuperacion-de-contrasena-para-administradores-y-clientes-en-laravel-13/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cómo implementar autenticación para dos tipos de usuarios en Laravel: administradores y clientes</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-autenticacion-para-dos-tipos-de-usuarios-en-laravel-administradores-y-clientes/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-autenticacion-para-dos-tipos-de-usuarios-en-laravel-administradores-y-clientes/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Wed, 20 May 2026 04:00:56 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=259</guid>

					<description><![CDATA[En este artículo veremos cómo estructurar en Laravel una autenticación separada para dos tipos de usuarios: administradores y clientes...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">En proyectos web donde existen diferentes tipos de usuarios, una sola autenticación puede quedarse corta. Un caso común es cuando el sistema tiene un panel administrativo para colaboradores y, al mismo tiempo, una página publica donde los clientes pueden iniciar sesión para consultar o gestionar su información.</p>



<p class="wp-block-paragraph">En este artículo veremos cómo estructurar en Laravel una autenticación separada para dos tipos de usuarios: <strong>administradores</strong> y <strong>clientes</strong>. La idea es mantener una arquitectura limpia, segura y fácil de mantener.</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">Contexto del proyecto</h2>



<p class="wp-block-paragraph">El escenario es el siguiente:</p>



<ul class="wp-block-list">
<li>El administrador accede desde una URL privada, por ejemplo: <code>/admin</code>.</li>



<li>Los clientes acceden desde el sitio público, por ejemplo: <code>/</code>.</li>



<li>Los administradores se almacenan en la tabla <code>users</code>.</li>



<li>Los clientes se almacenan en la tabla <code>customers</code>.</li>



<li>Cada sección usa vistas, layouts, assets y controladores separados.</li>



<li>Se utiliza Laravel Breeze como punto de partida, pero se adapta la estructura para soportar dos autenticaciones independientes.</li>
</ul>



<p class="wp-block-paragraph">La decisión principal fue no mezclar administradores y clientes en una misma tabla, ya que tienen responsabilidades, permisos y flujos completamente diferentes.</p>



<h2 class="wp-block-heading">Estructura recomendada del proyecto</h2>



<p class="wp-block-paragraph">Una estructura limpia puede organizarse así:</p>



<pre class="wp-block-code"><code>app/
├── Http/
│   ├── Controllers/
│   │   ├── Admin/
│   │   │   ├── Auth/
│   │   │   └── DashboardController.php
│   │   │
│   │   ├── Customer/
│   │   │   ├── Auth/
│   │   │   └── AccountController.php
│   │   │
│   │   └── Controller.php
│   │
│   ├── Requests/
│   │   ├── Admin/
│   │   │   └── Auth/
│   │   │       └── LoginRequest.php
│   │   │
│   │   └── Customer/
│   │       └── Auth/
│   │           └── LoginRequest.php
│
resources/
├── views/
│   ├── admin/
│   │   ├── auth/
│   │   ├── layouts/
│   │   └── dashboard/
│   │
│   └── customer/
│       ├── auth/
│       ├── layouts/
│       └── account/
│
routes/
├── admin.php
├── customer.php
├── web.php
└── console.php

public/
└── assets/
    ├── admin/
    └── customer/</code></pre>



<p class="wp-block-paragraph">Esta separación permite que cada sección tenga su propia lógica, sus propios recursos visuales y sus propios middlewares.</p>



<h2 class="wp-block-heading">Configuración de guards y providers</h2>



<p class="wp-block-paragraph">Laravel permite manejar múltiples sistemas de autenticación mediante <strong>guards</strong> y <strong>providers</strong>.</p>



<p class="wp-block-paragraph">En este caso usamos:</p>



<ul class="wp-block-list">
<li><code>web</code> para administradores.</li>



<li><code>customer</code> para clientes.</li>
</ul>



<p class="wp-block-paragraph">La configuración se realiza en <code>config/auth.php</code>:</p>



<pre class="wp-block-code"><code>&lt;?php

use App\Models\User;
use App\Models\Customer;

return &#91;

    'defaults' =&gt; &#91;
        'guard' =&gt; env('AUTH_GUARD', 'web'),
        'passwords' =&gt; env('AUTH_PASSWORD_BROKER', 'users'),
    ],

    'guards' =&gt; &#91;
        'web' =&gt; &#91;
            'driver' =&gt; 'session',
            'provider' =&gt; 'users',
        ],

        'customer' =&gt; &#91;
            'driver' =&gt; 'session',
            'provider' =&gt; 'customers',
        ],
    ],

    'providers' =&gt; &#91;
        'users' =&gt; &#91;
            'driver' =&gt; 'eloquent',
            'model' =&gt; User::class,
        ],

        'customers' =&gt; &#91;
            'driver' =&gt; 'eloquent',
            'model' =&gt; Customer::class,
        ],
    ],

    'passwords' =&gt; &#91;
        'users' =&gt; &#91;
            'provider' =&gt; 'users',
            'table' =&gt; env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'),
            'expire' =&gt; 60,
            'throttle' =&gt; 60,
        ],

        'customers' =&gt; &#91;
            'provider' =&gt; 'customers',
            'table' =&gt; env('CUSTOMER_AUTH_PASSWORD_RESET_TOKEN_TABLE', 'customer_password_reset_tokens'),
            'expire' =&gt; 60,
            'throttle' =&gt; 60,
        ],
    ],

    'password_timeout' =&gt; env('AUTH_PASSWORD_TIMEOUT', 10800),

];</code></pre>



<p class="wp-block-paragraph">Un detalle importante es no usar la misma variable <code>AUTH_MODEL</code> para ambos providers, porque podría provocar que los clientes intenten autenticarse con el modelo de usuarios administrativos.</p>



<h2 class="wp-block-heading">Modelo Customer autenticable</h2>



<p class="wp-block-paragraph">El modelo de cliente no debe extender directamente de <code>Model</code>, sino de <code>Authenticatable</code>, igual que el modelo <code>User</code>.</p>



<pre class="wp-block-code"><code>&lt;?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class Customer extends Authenticatable
{
    use Notifiable;

    protected $guard = 'customer';

    protected $fillable = &#91;
        'name',
        'last_name',
        'phone',
        'email',
        'password',
        'enabled',
    ];

    protected $hidden = &#91;
        'password',
        'remember_token',
    ];
}</code></pre>



<p class="wp-block-paragraph">Esto permite que Laravel pueda autenticar clientes usando el guard <code>customer</code>.</p>



<h2 class="wp-block-heading">Migraciones principales</h2>



<p class="wp-block-paragraph">Para los clientes se puede crear una tabla independiente:</p>



<pre class="wp-block-code"><code>Schema::create('customers', function (Blueprint $table) {
    $table-&gt;id();
    $table-&gt;string('name');
    $table-&gt;string('last_name');
    $table-&gt;char('phone', 10)-&gt;nullable();
    $table-&gt;string('email')-&gt;unique();
    $table-&gt;timestamp('email_verified_at')-&gt;nullable();
    $table-&gt;string('password');
    $table-&gt;rememberToken();
    $table-&gt;tinyInteger('enabled')-&gt;default(1);
    $table-&gt;timestamps();
});</code></pre>



<p class="wp-block-paragraph">También se puede manejar una tabla independiente para recuperación de contraseñas:</p>



<pre class="wp-block-code"><code>Schema::create('customer_password_reset_tokens', function (Blueprint $table) {
    $table-&gt;string('email')-&gt;primary();
    $table-&gt;string('token');
    $table-&gt;timestamp('created_at')-&gt;nullable();
});</code></pre>



<p class="wp-block-paragraph">En cuanto a sesiones, no es obligatorio crear una tabla diferente para clientes. Laravel usa la tabla <code>sessions</code> para guardar la sesión del navegador, no una sesión independiente por guard.</p>



<h2 class="wp-block-heading">Rutas separadas</h2>



<p class="wp-block-paragraph">Una buena práctica es separar las rutas del administrador y del cliente.</p>



<p class="wp-block-paragraph">Ejemplo de <code>routes/admin.php</code>:</p>



<pre class="wp-block-code"><code>use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Admin\DashboardController;

Route::middleware('admin.guest')->group(function () {
    Route::get('/login', &#91;AuthenticatedSessionController::class, 'create'])
        ->name('login');

    Route::post('/login', &#91;AuthenticatedSessionController::class, 'store'])
        ->name('login.store');
});

Route::middleware('admin.auth')->group(function () {
    Route::get('/dashboard', &#91;DashboardController::class, 'index'])
        ->name('dashboard');

    Route::post('/logout', &#91;AuthenticatedSessionController::class, 'destroy'])
        ->name('logout');
});</code></pre>



<p class="wp-block-paragraph">Ejemplo de <code>routes/customer.php</code>:</p>



<pre class="wp-block-code"><code>use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Customer\Auth\AuthenticatedSessionController;
use App\Http\Controllers\Customer\HomeController;
use App\Http\Controllers\Customer\AccountController;

Route::get('/', &#91;HomeController::class, 'index'])
    ->name('home');

Route::middleware('customer.guest')->group(function () {
    Route::get('/login', &#91;AuthenticatedSessionController::class, 'create'])
        ->name('login');

    Route::post('/login', &#91;AuthenticatedSessionController::class, 'store'])
        ->name('login.store');
});

Route::middleware('customer.auth')->group(function () {
    Route::get('/mi-cuenta', &#91;AccountController::class, 'index'])
        ->name('account');

    Route::post('/logout', &#91;AuthenticatedSessionController::class, 'destroy'])
        ->name('logout');
});</code></pre>



<h2 class="wp-block-heading">Cargar rutas desde bootstrap/app.php</h2>



<p class="wp-block-paragraph">En Laravel moderno, las rutas adicionales pueden registrarse desde <code>bootstrap/app.php</code>:</p>



<pre class="wp-block-code"><code>use Illuminate\Support\Facades\Route;

->withRouting(
    web: __DIR__.'/../routes/web.php',
    commands: __DIR__.'/../routes/console.php',
    then: function () {
        Route::middleware('web')
            ->prefix('admin')
            ->name('admin.')
            ->group(base_path('routes/admin.php'));

        Route::middleware('web')
            ->name('customer.')
            ->group(base_path('routes/customer.php'));
    },
)</code></pre>



<p class="wp-block-paragraph">Con esta configuración se obtienen rutas como:</p>



<ul class="wp-block-list">
<li><code>/admin/login</code> → <code>admin.login</code></li>



<li><code>/admin/dashboard</code> → <code>admin.dashboard</code></li>



<li><code>/login</code> → <code>customer.login</code></li>



<li><code>/mi-cuenta</code> → <code>customer.account</code></li>
</ul>



<h2 class="wp-block-heading">Middlewares propios por tipo de usuario</h2>



<p class="wp-block-paragraph">Para evitar lógica condicional dentro de <code>bootstrap/app.php</code>, se pueden crear middlewares específicos para cada sección.</p>



<p class="wp-block-paragraph">Por ejemplo:</p>



<ul class="wp-block-list">
<li><code>admin.auth</code>: valida que el usuario admin esté autenticado y activo.</li>



<li><code>admin.guest</code>: evita que un admin autenticado vuelva al login.</li>



<li><code>customer.auth</code>: valida que el cliente esté autenticado y activo.</li>



<li><code>customer.guest</code>: evita que un cliente autenticado vuelva al login.</li>
</ul>



<p class="wp-block-paragraph">Middleware de administrador autenticado:</p>



<pre class="wp-block-code"><code>&lt;?php

namespace App\Http\Middleware\Admin;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class EnsureAdminIsAuthenticated
{
    public function handle(Request $request, Closure $next): Response
    {
        if (! Auth::guard('web')-&gt;check()) {
            return redirect()-&gt;route('admin.login');
        }

        $user = Auth::guard('web')-&gt;user();

        if (! $user-&gt;enabled) {
            Auth::guard('web')-&gt;logout();

            $request-&gt;session()-&gt;invalidate();
            $request-&gt;session()-&gt;regenerateToken();

            return redirect()
                -&gt;route('admin.login')
                -&gt;withErrors(&#91;
                    'email' =&gt; 'Tu cuenta se encuentra deshabilitada.',
                ]);
        }

        return $next($request);
    }
}</code></pre>



<p class="wp-block-paragraph">Middleware de cliente autenticado:</p>



<pre class="wp-block-code"><code>&lt;?php

namespace App\Http\Middleware\Customer;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class EnsureCustomerIsAuthenticated
{
    public function handle(Request $request, Closure $next): Response
    {
        if (! Auth::guard('customer')-&gt;check()) {
            return redirect()-&gt;route('customer.login');
        }

        $customer = Auth::guard('customer')-&gt;user();

        if (! $customer-&gt;enabled) {
            Auth::guard('customer')-&gt;logout();

            $request-&gt;session()-&gt;invalidate();
            $request-&gt;session()-&gt;regenerateToken();

            return redirect()
                -&gt;route('customer.login')
                -&gt;withErrors(&#91;
                    'email' =&gt; 'Tu cuenta se encuentra deshabilitada.',
                ]);
        }

        return $next($request);
    }
}</code></pre>



<p class="wp-block-paragraph">Después se registran los alias de middleware en <code>bootstrap/app.php</code>:</p>



<pre class="wp-block-code"><code>use Illuminate\Foundation\Configuration\Middleware;
use App\Http\Middleware\Admin\EnsureAdminIsAuthenticated;
use App\Http\Middleware\Admin\RedirectIfAdminAuthenticated;
use App\Http\Middleware\Customer\EnsureCustomerIsAuthenticated;
use App\Http\Middleware\Customer\RedirectIfCustomerAuthenticated;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias(&#91;
        'admin.auth' => EnsureAdminIsAuthenticated::class,
        'admin.guest' => RedirectIfAdminAuthenticated::class,

        'customer.auth' => EnsureCustomerIsAuthenticated::class,
        'customer.guest' => RedirectIfCustomerAuthenticated::class,
    ]);
})</code></pre>



<h2 class="wp-block-heading">LoginRequest para clientes</h2>



<p class="wp-block-paragraph">Uno de los errores más comunes al trabajar con múltiples guards es usar <code>Auth::attempt()</code> sin especificar guard. Cuando se hace eso, Laravel utiliza el guard por defecto, normalmente <code>web</code>.</p>



<p class="wp-block-paragraph">Para el login de clientes debe usarse:</p>



<pre class="wp-block-code"><code>Auth::guard('customer')-&gt;attempt($credentials, $this-&gt;boolean('remember'))</code></pre>



<p class="wp-block-paragraph">Ejemplo:</p>



<pre class="wp-block-code"><code>public function authenticate(): void
{
    $this-&gt;ensureIsNotRateLimited();

    $credentials = $this-&gt;only('email', 'password');
    $credentials&#91;'enabled'] = 1;

    if (! Auth::guard('customer')-&gt;attempt($credentials, $this-&gt;boolean('remember'))) {
        RateLimiter::hit($this-&gt;throttleKey());

        throw ValidationException::withMessages(&#91;
            'email' =&gt; trans('auth.failed'),
        ]);
    }

    Auth::guard('web')-&gt;logout();

    RateLimiter::clear($this-&gt;throttleKey());
}</code></pre>



<p class="wp-block-paragraph">En este ejemplo, después de iniciar sesión como cliente, se cierra cualquier sesión activa del administrador en el mismo navegador.</p>



<h2 class="wp-block-heading">LoginRequest para administradores</h2>



<p class="wp-block-paragraph">Para el administrador se hace lo mismo, pero usando el guard <code>web</code>:</p>



<pre class="wp-block-code"><code>public function authenticate(): void
{
    $this-&gt;ensureIsNotRateLimited();

    $credentials = $this-&gt;only('email', 'password');
    $credentials&#91;'enabled'] = 1;

    if (! Auth::guard('web')-&gt;attempt($credentials, $this-&gt;boolean('remember'))) {
        RateLimiter::hit($this-&gt;throttleKey());

        throw ValidationException::withMessages(&#91;
            'email' =&gt; trans('auth.failed'),
        ]);
    }

    Auth::guard('customer')-&gt;logout();

    RateLimiter::clear($this-&gt;throttleKey());
}</code></pre>



<p class="wp-block-paragraph">De esta manera se evita que el mismo navegador mantenga una sesión de administrador y una sesión de cliente al mismo tiempo.</p>



<h2 class="wp-block-heading">Uso correcto en Blade</h2>



<p class="wp-block-paragraph">Otro detalle importante es que la directiva <code>@auth</code> usa el guard por defecto si no se especifica uno. Por eso, en vistas de cliente se debe indicar explícitamente el guard.</p>



<p class="wp-block-paragraph">Para clientes:</p>



<pre class="wp-block-code"><code>@auth('customer')
    Hola, {{ auth('customer')->user()->name }}
@endauth

@guest('customer')
    &lt;a href="{{ route('customer.login') }}">Iniciar sesión&lt;/a>
@endguest</code></pre>



<p class="wp-block-paragraph">Para administradores:</p>



<pre class="wp-block-code"><code>@auth('web')
    Hola, {{ auth('web')->user()->name }}
@endauth

@guest('web')
    &lt;a href="{{ route('admin.login') }}">Iniciar sesión&lt;/a>
@endguest</code></pre>



<h2 class="wp-block-heading">Sesiones en base de datos</h2>



<p class="wp-block-paragraph">Cuando se usa <code>SESSION_DRIVER=database</code>, Laravel guarda la sesión del navegador en la tabla <code>sessions</code>. Es importante entender que no se crea una sesión independiente por guard.</p>



<p class="wp-block-paragraph">Si se inicia sesión como administrador y después como cliente en el mismo navegador, Laravel puede seguir usando el mismo registro de sesión. Lo importante es que el guard correcto esté autenticado y el otro haya sido cerrado.</p>



<p class="wp-block-paragraph">Para validar:</p>



<pre class="wp-block-code"><code>auth('web')-&gt;check();   // Admin
auth('customer')-&gt;check(); // Cliente</code></pre>



<p class="wp-block-paragraph">También es normal que el registro en la tabla <code>sessions</code> permanezca por un tiempo aunque el usuario haya cerrado sesión. Laravel limpia las sesiones expiradas según su configuración de expiración.</p>



<h2 class="wp-block-heading">Extras</h2>



<h3 class="wp-block-heading">Assets separados con Vite</h3>



<p class="wp-block-paragraph">Si cada sección utiliza un template diferente, también conviene separar los assets.</p>



<pre class="wp-block-code"><code>resources/
├── admin/
│   ├── css/
│   │   └── app.css
│   └── js/
│       └── app.js
│
└── customer/
    ├── css/
    │   └── app.css
    └── js/
        └── app.js</code></pre>



<p class="wp-block-paragraph">Ejemplo de <code>vite.config.js</code>:</p>



<pre class="wp-block-code"><code>import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';

export default defineConfig({
    plugins: &#91;
        laravel({
            input: &#91;
                'resources/admin/css/app.css',
                'resources/admin/js/app.js',

                'resources/customer/css/app.css',
                'resources/customer/js/app.js',
            ],
            refresh: true,
        }),
    ],
});</code></pre>



<p class="wp-block-paragraph">En el layout de administrador:</p>



<pre class="wp-block-code"><code>@vite(&#91;
    'resources/admin/css/app.css',
    'resources/admin/js/app.js'
])</code></pre>



<p class="wp-block-paragraph">En el layout de cliente:</p>



<pre class="wp-block-code"><code>@vite(&#91;
    'resources/customer/css/app.css',
    'resources/customer/js/app.js'
])</code></pre>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">Implementar autenticación para dos tipos de usuarios en Laravel requiere separar responsabilidades desde el inicio. La clave está en no mezclar modelos, guards, rutas ni vistas.</p>



<p class="wp-block-paragraph">Una arquitectura limpia para este caso sería:</p>



<ul class="wp-block-list">
<li><code>users</code> para administradores.</li>



<li><code>customers</code> para clientes.</li>



<li><code>web</code> como guard de administradores.</li>



<li><code>customer</code> como guard de clientes.</li>



<li><code>resources/views/admin</code> para el panel administrativo.</li>



<li><code>resources/views/customer</code> para el portal público.</li>



<li><code>routes/admin.php</code> y <code>routes/customer.php</code> para separar rutas.</li>



<li>Middlewares propios para controlar accesos y redirecciones.</li>
</ul>



<p class="wp-block-paragraph">Esta estructura evita confusiones, mejora la seguridad y permite que el proyecto crezca de forma ordenada.</p>



<p class="wp-block-paragraph">En sistemas donde existen usuarios internos y clientes finales, separar la autenticación no solo es una buena práctica técnica: también facilita el mantenimiento, las pruebas y la evolución del proyecto.</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/como-implementar-autenticacion-para-dos-tipos-de-usuarios-en-laravel-administradores-y-clientes/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Cómo instalar y usar una versión diferente de PHP con Laravel Valet en Mac</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/como-instalar-y-usar-una-version-diferente-de-php-con-laravel-valet-en-mac/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/como-instalar-y-usar-una-version-diferente-de-php-con-laravel-valet-en-mac/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Mon, 04 May 2026 06:35:18 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=248</guid>

					<description><![CDATA[Cuando trabajamos con Laravel en macOS usando Laravel Valet, una de las ventajas más importantes es...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Cuando trabajamos con Laravel en macOS usando <strong>Laravel Valet</strong>, una de las ventajas más importantes es que podemos cambiar la versión de PHP de forma relativamente sencilla.</p>



<p class="wp-block-paragraph">Esto es muy útil cuando un proyecto requiere una versión específica de PHP o cuando algún paquete de Composer todavía no es compatible con la versión más reciente.</p>



<p class="wp-block-paragraph">En este artículo veremos cómo instalar y usar una versión diferente de PHP con Laravel Valet, tomando como ejemplo un problema común al instalar paquetes como <code>maatwebsite/excel</code>.</p>



<h2 class="wp-block-heading">El problema: incompatibilidad entre paquetes y versión de PHP</h2>



<p class="wp-block-paragraph">En nuestro caso, al intentar instalar <code>maatwebsite/excel</code>, Composer mostró un error relacionado con <code>phpoffice/phpspreadsheet</code>:</p>



<pre class="wp-block-code"><code>phpoffice/phpspreadsheet 1.30.4 requires php &gt;=7.4.0 &lt;8.5.0 -&gt; your php version (8.5.5) does not satisfy that requirement.</code></pre>



<p class="wp-block-paragraph">El mensaje es bastante claro: la versión instalada de PHP era <strong>8.5.5</strong>, pero <code>phpoffice/phpspreadsheet</code> en la versión requerida por el proyecto solo permite PHP menor a <strong>8.5.0</strong>.</p>



<p class="wp-block-paragraph">Esto no significa que Laravel Valet esté mal configurado. Simplemente quiere decir que el paquete aún no es compatible con la versión de PHP que estamos usando.</p>



<p class="wp-block-paragraph"><code>maatwebsite/excel</code> utiliza <code>phpoffice/phpspreadsheet</code> como dependencia para trabajar con archivos Excel y CSV.</p>



<h2 class="wp-block-heading">¿Por qué conviene cambiar de PHP en lugar de forzar Composer?</h2>



<p class="wp-block-paragraph">Cuando Composer marca un error de compatibilidad, una tentación común es usar banderas como:</p>



<pre class="wp-block-code"><code>composer require paquete --ignore-platform-reqs</code></pre>



<p class="wp-block-paragraph">Aunque esto puede “saltarse” temporalmente la validación, no es recomendable como solución principal.</p>



<p class="wp-block-paragraph">El problema es que Composer dejaría instalar un paquete que oficialmente no soporta tu versión de PHP. Eso puede provocar errores más adelante en ejecución, especialmente cuando se trata de librerías que manipulan archivos, fechas, memoria, formatos o extensiones de PHP.</p>



<p class="wp-block-paragraph">La solución más limpia es usar una versión de PHP compatible con el proyecto.</p>



<p class="wp-block-paragraph">En este caso, la solución fue bajar de PHP 8.5 a <strong>PHP 8.4</strong>.</p>



<h2 class="wp-block-heading">Verificar la versión actual de PHP</h2>



<p class="wp-block-paragraph">Antes de cambiar nada, conviene revisar qué versión está usando nuestra terminal:</p>



<pre class="wp-block-code"><code>php -v</code></pre>



<p class="wp-block-paragraph">También podemos revisar qué versión de PHP está usando Valet:</p>



<pre class="wp-block-code"><code>valet which-php</code></pre>



<p class="wp-block-paragraph">Esto ayuda a confirmar si Composer, Laravel y Valet están trabajando con la versión esperada.</p>



<h2 class="wp-block-heading">Instalar PHP 8.4 con Homebrew</h2>



<p class="wp-block-paragraph">Si estamos usando Valet en macOS, lo normal es instalar PHP mediante Homebrew.</p>



<p class="wp-block-paragraph">Para instalar PHP 8.4 ejecutamos:</p>



<pre class="wp-block-code"><code>brew install php@8.4</code></pre>



<p class="wp-block-paragraph">Si ya lo teníamos instalado, podemos asegurarnos de que esté actualizado con:</p>



<pre class="wp-block-code"><code>brew update
brew upgrade php@8.4</code></pre>



<h2 class="wp-block-heading">Cambiar Laravel Valet a PHP 8.4</h2>



<p class="wp-block-paragraph">Laravel Valet permite cambiar la versión global de PHP con el comando <code>valet use</code>.</p>



<p class="wp-block-paragraph">Para cambiar Valet a PHP 8.4:</p>



<pre class="wp-block-code"><code>valet use php@8.4</code></pre>



<p class="wp-block-paragraph">Se reiniciará Valet de manera automática, si no lo hace lo reiniciamos nosotros:</p>



<pre class="wp-block-code"><code>valet restart</code></pre>



<p class="wp-block-paragraph">Y validamos:</p>



<pre class="wp-block-code"><code>php -v
valet which-php</code></pre>



<p class="wp-block-paragraph">La salida debería mostrar una versión PHP 8.4.x.</p>



<h2 class="wp-block-heading">Si la terminal sigue usando otra versión</h2>



<p class="wp-block-paragraph">Puede pasar que Valet ya esté usando PHP 8.4, pero la terminal siga apuntando a PHP 8.5.</p>



<p class="wp-block-paragraph">En ese caso, podemos forzar el link de Homebrew:</p>



<pre class="wp-block-code"><code>brew unlink php
brew link --force --overwrite php@8.4</code></pre>



<p class="wp-block-paragraph">Luego volvemos a validar:</p>



<pre class="wp-block-code"><code>php -v</code></pre>



<p class="wp-block-paragraph">Si todo está correcto, Composer ya debería detectar PHP 8.4.</p>



<h2 class="wp-block-heading">Instalar nuevamente el paquete</h2>



<p class="wp-block-paragraph">Una vez cambiada la versión de PHP, regresamos al proyecto Laravel:</p>



<pre class="wp-block-code"><code>cd ~/Developments/LaravelProjects/nombre-del-proyecto</code></pre>



<p class="wp-block-paragraph">Y ejecutamos:</p>



<pre class="wp-block-code"><code>composer require maatwebsite/excel</code></pre>



<p class="wp-block-paragraph">Si el problema era la incompatibilidad con PHP 8.5, ahora Composer debería poder resolver las dependencias correctamente.</p>



<h2 class="wp-block-heading">Usar una versión de PHP solo para un proyecto</h2>



<p class="wp-block-paragraph">Una opción muy útil de Valet es aislar una versión de PHP por proyecto.</p>



<p class="wp-block-paragraph">Esto sirve cuando tenemos varios proyectos con diferentes necesidades. Por ejemplo:</p>



<ul class="wp-block-list">
<li>Un proyecto moderno usando PHP 8.4.</li>



<li>Un proyecto legacy usando PHP 8.2.</li>



<li>Un proyecto experimental usando PHP 8.5.</li>
</ul>



<p class="wp-block-paragraph">Para aislar un proyecto con PHP 8.4:</p>



<pre class="wp-block-code"><code>cd ~/Developments/LaravelProjects/foresta
valet isolate php@8.4</code></pre>



<p class="wp-block-paragraph">Para ver los sitios aislados:</p>



<pre class="wp-block-code"><code>valet isolated</code></pre>



<p class="wp-block-paragraph">Para quitar el aislamiento y volver a usar la versión global de Valet:</p>



<pre class="wp-block-code"><code>valet unisolate</code></pre>



<p class="wp-block-paragraph">Esta opción es muy práctica porque evita estar cambiando la versión global de PHP cada vez que trabajamos en proyectos distintos.</p>



<h2 class="wp-block-heading">Crear un archivo .valetrc</h2>



<p class="wp-block-paragraph">Otra alternativa es crear un archivo <code>.valetrc</code> dentro del proyecto indicando la versión de PHP que debe usar.</p>



<p class="wp-block-paragraph">Dentro del proyecto:</p>



<pre class="wp-block-code"><code>touch .valetrc</code></pre>



<p class="wp-block-paragraph">Y agregamos:</p>



<pre class="wp-block-code"><code>php=php@8.4</code></pre>



<p class="wp-block-paragraph">Después podemos ejecutar:</p>



<pre class="wp-block-code"><code>valet use</code></pre>



<p class="wp-block-paragraph">Valet leerá el archivo y aplicará la versión indicada.</p>



<h2 class="wp-block-heading">Comandos útiles de diagnóstico</h2>



<p class="wp-block-paragraph">Cuando Composer marque errores de compatibilidad, estos comandos ayudan bastante:</p>



<pre class="wp-block-code"><code>php -v</code></pre>



<pre class="wp-block-code"><code>valet which-php</code></pre>



<pre class="wp-block-code"><code>composer check-platform-reqs</code></pre>



<pre class="wp-block-code"><code>composer why-not maatwebsite/excel</code></pre>



<p class="wp-block-paragraph">También podemos revisar qué paquetes están bloqueando una instalación específica:</p>



<pre class="wp-block-code"><code>composer why-not phpoffice/phpspreadsheet</code></pre>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">Laravel Valet facilita bastante el manejo de versiones de PHP en macOS. Cuando un paquete no es compatible con la versión actual, lo ideal no es forzar Composer, sino revisar los requisitos y utilizar una versión de PHP compatible.</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/como-instalar-y-usar-una-version-diferente-de-php-con-laravel-valet-en-mac/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel Valet en Mac</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/laravel-valet-en-mac/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/laravel-valet-en-mac/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Sat, 02 May 2026 05:55:33 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=246</guid>

					<description><![CDATA[En este artículo veremos qué es Laravel Valet, cómo instalarlo, cómo configurarlo y cómo solucionar algunos errores comunes que pueden aparecer durante el proceso.]]></description>
										<content:encoded><![CDATA[
<h2 class="wp-block-heading">Guía práctica para entorno de desarrollo moderno</h2>



<p class="wp-block-paragraph">Si vienes de trabajar con herramientas como <strong>MAMP</strong>, probablemente estés acostumbrado a levantar y apagar servicios manualmente cada vez que desarrollas.</p>



<p class="wp-block-paragraph">Pero cuando empiezas a trabajar con <strong>Laravel Valet</strong>, el flujo cambia bastante: todo es más rápido, más limpio y mucho más cómodo para proyectos Laravel en macOS.</p>



<p class="wp-block-paragraph">En este artículo veremos qué es Laravel Valet, cómo instalarlo, cómo configurarlo y cómo solucionar algunos errores comunes que pueden aparecer durante el proceso.</p>



<h2 class="wp-block-heading">¿Qué es Laravel Valet?</h2>



<p class="wp-block-paragraph"><strong>Laravel Valet</strong> es un entorno de desarrollo local para macOS que permite ejecutar proyectos PHP y Laravel sin necesidad de configurar servidores virtuales manualmente.</p>



<p class="wp-block-paragraph">Valet utiliza principalmente:</p>



<ul class="wp-block-list">
<li><strong>Nginx</strong>, como servidor web.</li>



<li><strong>dnsmasq</strong>, para resolver dominios locales.</li>



<li><strong>PHP</strong>, instalado normalmente mediante Homebrew.</li>
</ul>



<p class="wp-block-paragraph">Con Valet puedes acceder a tus proyectos usando dominios como:</p>



<pre class="wp-block-code"><code>http:&#47;&#47;proyecto.test</code></pre>



<p class="wp-block-paragraph">En lugar de depender de rutas como:</p>



<pre class="wp-block-code"><code>http:&#47;&#47;localhost:8000</code></pre>



<h2 class="wp-block-heading">Instalación base de Laravel Valet</h2>



<p class="wp-block-paragraph">Antes de instalar Valet es recomendable contar con:</p>



<ul class="wp-block-list">
<li>Homebrew instalado.</li>



<li>PHP instalado.</li>



<li>Composer instalado.</li>
</ul>



<p class="wp-block-paragraph">Para instalar Valet ejecutamos:</p>



<pre class="wp-block-code"><code>composer global require laravel/valet</code></pre>



<p class="wp-block-paragraph">Después inicializamos Valet con:</p>



<pre class="wp-block-code"><code>valet install</code></pre>



<p class="wp-block-paragraph">Este comando configura los servicios necesarios para que Valet funcione correctamente en segundo plano.</p>



<h3 class="wp-block-heading">Error común: zsh: command not found: valet</h3>



<p class="wp-block-paragraph">Uno de los errores más comunes después de instalar Valet es:</p>



<pre class="wp-block-code"><code>zsh: command not found: valet</code></pre>



<p class="wp-block-paragraph">Esto normalmente significa que Valet sí fue instalado, pero la ruta global de Composer no está agregada al PATH del sistema.</p>



<p class="wp-block-paragraph">Para encontrar la ruta global de Composer ejecutamos:</p>



<pre class="wp-block-code"><code>composer global config bin-dir --absolute</code></pre>



<p class="wp-block-paragraph">El resultado suele ser algo parecido a:</p>



<pre class="wp-block-code"><code>/Users/tu_usuario/.config/composer/vendor/bin</code></pre>



<p class="wp-block-paragraph">Después agregamos esa ruta al archivo <code>.zshrc</code>:</p>



<pre class="wp-block-code"><code>nano ~/.zshrc</code></pre>



<p class="wp-block-paragraph">Y añadimos:</p>



<pre class="wp-block-code"><code>export PATH="$HOME/.config/composer/vendor/bin:$PATH"</code></pre>



<p class="wp-block-paragraph">Finalmente recargamos la configuración:</p>



<pre class="wp-block-code"><code>source ~/.zshrc</code></pre>



<p class="wp-block-paragraph">Y validamos:</p>



<pre class="wp-block-code"><code>valet --version</code></pre>



<h2 class="wp-block-heading">Organización de proyectos con &#8220;valet park&#8221;</h2>



<p class="wp-block-paragraph">Una de las funciones más útiles de Valet es &#8220;<code>valet park"</code>.</p>



<p class="wp-block-paragraph">Este comando le indica a Valet que todos los proyectos dentro de una carpeta deben estar disponibles automáticamente como dominios <code>.test</code>.</p>



<p class="wp-block-paragraph">Por ejemplo:</p>



<pre class="wp-block-code"><code>cd ~/Developments/LaravelProjects
valet park</code></pre>



<p class="wp-block-paragraph">Si dentro de esa carpeta tenemos:</p>



<pre class="wp-block-code"><code>tu-proyecto-1
tu-proyecto-2
tu-proyecto-3</code></pre>



<p class="wp-block-paragraph">Entonces podremos acceder a:</p>



<pre class="wp-block-code"><code>http:&#47;&#47;tu-proyecto-1.test
http://tu-proyecto-2.test
http://tu-proyecto-3.test</code></pre>



<h2 class="wp-block-heading">Comandos útiles de Valet</h2>



<p class="wp-block-paragraph">Algunos comandos básicos para controlar Valet son:</p>



<pre class="wp-block-code"><code>valet start</code></pre>



<p class="wp-block-paragraph">Inicia los servicios de Valet.</p>



<pre class="wp-block-code"><code>valet stop</code></pre>



<p class="wp-block-paragraph">Detiene los servicios de Valet.</p>



<pre class="wp-block-code"><code>valet restart</code></pre>



<p class="wp-block-paragraph">Reinicia Valet. Es muy útil cuando algo no responde correctamente.</p>



<pre class="wp-block-code"><code>valet paths</code></pre>



<p class="wp-block-paragraph">Muestra las carpetas que están configuradas con <code>valet park</code>.</p>



<pre class="wp-block-code"><code>valet forget</code></pre>



<p class="wp-block-paragraph">Elimina el park de la carpeta actual.</p>



<h2 class="wp-block-heading">Configuración de Node con nvm</h2>



<p class="wp-block-paragraph">Para trabajar con Laravel moderno también necesitamos Node.js, ya que Laravel usa Vite para compilar CSS y JavaScript.</p>



<p class="wp-block-paragraph">Una buena práctica es manejar Node con <strong>nvm</strong>, ya que permite instalar y cambiar versiones fácilmente.</p>



<p class="wp-block-paragraph">Instalamos nvm con Homebrew:</p>



<pre class="wp-block-code"><code>brew install nvm</code></pre>



<p class="wp-block-paragraph">Creamos la carpeta de configuración:</p>



<pre class="wp-block-code"><code>mkdir ~/.nvm</code></pre>



<p class="wp-block-paragraph">Después agregamos esto al archivo <code>.zshrc</code>:</p>



<pre class="wp-block-code"><code>export NVM_DIR="$HOME/.nvm"
source $(brew --prefix nvm)/nvm.sh</code></pre>



<p class="wp-block-paragraph">Recargamos:</p>



<pre class="wp-block-code"><code>source ~/.zshrc</code></pre>



<p class="wp-block-paragraph">Instalamos una versión de Node:</p>



<pre class="wp-block-code"><code>nvm install 22
nvm use 22</code></pre>



<p class="wp-block-paragraph">Y la dejamos como versión por defecto:</p>



<pre class="wp-block-code"><code>nvm alias default 22</code></pre>



<p class="wp-block-paragraph">Si queremos la última versión estable de Node el comando sería:</p>



<pre class="wp-block-code"><code>nvm install --lts</code></pre>



<h3 class="wp-block-heading">Error común: env: node: No such file or directory</h3>



<p class="wp-block-paragraph">Este error aparece cuando el sistema no encuentra Node:</p>



<pre class="wp-block-code"><code>env: node: No such file or directory</code></pre>



<p class="wp-block-paragraph">Generalmente ocurre porque nvm no se cargó correctamente en la terminal.</p>



<p class="wp-block-paragraph">Para solucionarlo podemos ejecutar:</p>



<pre class="wp-block-code"><code>nvm use default</code></pre>



<p class="wp-block-paragraph">Y validar:</p>



<pre class="wp-block-code"><code>node -v
npm -v</code></pre>



<p class="wp-block-paragraph">Si el problema continúa, hay que revisar que la configuración de nvm esté correctamente agregada en <code>~/.zshrc</code>.</p>



<h2 class="wp-block-heading">Crear un proyecto Laravel</h2>



<p class="wp-block-paragraph">Para crear proyectos Laravel de forma más rápida podemos instalar el instalador oficial:</p>



<pre class="wp-block-code"><code>composer global require laravel/installer</code></pre>



<p class="wp-block-paragraph">Este comando habilita el comando:</p>



<pre class="wp-block-code"><code>laravel new nombre-proyecto</code></pre>



<p class="wp-block-paragraph">Por ejemplo:</p>



<pre class="wp-block-code"><code>cd ~/Developments/LaravelProjects
laravel new tu-proyecto-1</code></pre>



<p class="wp-block-paragraph">Después podemos abrirlo con:</p>



<pre class="wp-block-code"><code>cd tu-proyecto-1
valet open</code></pre>



<p class="wp-block-paragraph">Y el proyecto estará disponible en:</p>



<pre class="wp-block-code"><code>http:&#47;&#47;tu-proyecto-1.test</code></pre>



<h2 class="wp-block-heading">Frontend con Vite</h2>



<p class="wp-block-paragraph">Laravel utiliza Vite para compilar los archivos CSS y JavaScript del proyecto.</p>



<p class="wp-block-paragraph">Después de crear el proyecto debemos ejecutar:</p>



<pre class="wp-block-code"><code>npm install
npm run dev</code></pre>



<p class="wp-block-paragraph">El comando <code>npm install</code> instala las dependencias definidas en <code>package.json</code>.</p>



<p class="wp-block-paragraph">El comando <code>npm run dev</code> levanta Vite en modo desarrollo, permitiendo compilar assets y trabajar con recarga automática (hot reload).</p>



<h3 class="wp-block-heading">Error común: Failed to resolve import &#8220;./bootstrap&#8221;</h3>



<p class="wp-block-paragraph">Este error puede aparecer al ejecutar Vite:</p>



<pre class="wp-block-code"><code>Failed to resolve import "./bootstrap" from "resources/js/app.js"</code></pre>



<p class="wp-block-paragraph">Significa que Laravel está intentando importar este archivo:</p>



<pre class="wp-block-code"><code>resources/js/bootstrap.js</code></pre>



<p class="wp-block-paragraph">Pero no existe o no fue generado correctamente.</p>



<p class="wp-block-paragraph">Primero podemos intentar reinstalar dependencias:</p>



<pre class="wp-block-code"><code>rm -rf node_modules package-lock.json
npm install
npm run dev</code></pre>



<p class="wp-block-paragraph">Si el archivo sigue sin existir, se puede crear manualmente:</p>



<pre class="wp-block-code"><code>touch resources/js/bootstrap.js</code></pre>



<p class="wp-block-paragraph">Y agregar:</p>



<pre class="wp-block-code"><code>import axios from 'axios';

window.axios = axios;

window.axios.defaults.headers.common&#91;'X-Requested-With'] = 'XMLHttpRequest';</code></pre>



<h3 class="wp-block-heading">Error común: Failed to resolve import &#8220;axios&#8221;</h3>



<p class="wp-block-paragraph">Otro error común es:</p>



<pre class="wp-block-code"><code>Failed to resolve import "axios" from "resources/js/bootstrap.js"</code></pre>



<p class="wp-block-paragraph">Esto significa que el archivo <code>bootstrap.js</code> intenta importar Axios, pero Axios no está instalado.</p>



<p class="wp-block-paragraph">La solución es:</p>



<pre class="wp-block-code"><code>npm install axios
npm run dev</code></pre>



<h2 class="wp-block-heading">Configurar MySQL sin depender de MAMP</h2>



<p class="wp-block-paragraph">Si queremos trabajar sin depender de MAMP, podemos instalar MySQL mediante Homebrew:</p>



<pre class="wp-block-code"><code>brew install mysql</code></pre>



<p class="wp-block-paragraph">Para iniciar el servicio:</p>



<pre class="wp-block-code"><code>brew services start mysql</code></pre>



<p class="wp-block-paragraph">Para detenerlo:</p>



<pre class="wp-block-code"><code>brew services stop mysql</code></pre>



<p class="wp-block-paragraph">Podemos entrar a MySQL con:</p>



<pre class="wp-block-code"><code>mysql -u root</code></pre>



<p class="wp-block-paragraph">Y crear una base de datos:</p>



<pre class="wp-block-code"><code>CREATE DATABASE tu-proyecto-1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;</code></pre>



<h3 class="wp-block-heading">Agregar contraseña al usuario root de MySQL</h3>



<p class="wp-block-paragraph">Para asignar contraseña al usuario root podemos ejecutar:</p>



<pre class="wp-block-code"><code>ALTER USER 'root'@'localhost' IDENTIFIED BY 'TuPasswordSeguro123!';
FLUSH PRIVILEGES;</code></pre>



<p class="wp-block-paragraph">Después configuramos el archivo <code>.env</code> de Laravel:</p>



<pre class="wp-block-code"><code>DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=tu-proyecto-1
DB_USERNAME=root
DB_PASSWORD=TuPasswordSeguro123!</code></pre>



<h3 class="wp-block-heading">Error común: Plugin &#8216;mysql_native_password&#8217; is not loaded</h3>



<p class="wp-block-paragraph">En versiones recientes de MySQL puede aparecer este error:</p>



<pre class="wp-block-code"><code>ERROR 1524 (HY000): Plugin 'mysql_native_password' is not loaded</code></pre>



<p class="wp-block-paragraph">Esto ocurre porque MySQL moderno ya no utiliza ese plugin por defecto. Actualmente se usa <code>caching_sha2_password</code>.</p>



<p class="wp-block-paragraph">La solución es no forzar <code>mysql_native_password</code> y usar:</p>



<pre class="wp-block-code"><code>ALTER USER 'root'@'localhost' IDENTIFIED BY 'TuPasswordSeguro123!';
FLUSH PRIVILEGES;</code></pre>



<h2 class="wp-block-heading">Flujo recomendado de trabajo</h2>



<p class="wp-block-paragraph">Una vez configurado el entorno, el flujo diario puede ser muy sencillo.</p>



<h3 class="wp-block-heading">Para iniciar el entorno</h3>



<pre class="wp-block-code"><code>brew services start mysql
valet start
cd ~/Developments/LaravelProjects/tu-proyecto-1
npm run dev</code></pre>



<h3 class="wp-block-heading">Para apagar el entorno</h3>



<p class="wp-block-paragraph">Primero detenemos Vite con:</p>



<pre class="wp-block-code"><code>Ctrl + C</code></pre>



<p class="wp-block-paragraph">Después detenemos MySQL:</p>



<pre class="wp-block-code"><code>brew services stop mysql</code></pre>



<p class="wp-block-paragraph">Y opcionalmente Valet:</p>



<pre class="wp-block-code"><code>valet stop</code></pre>



<h2 class="wp-block-heading">Tip pro: crear alias para iniciar y detener el entorno</h2>



<p class="wp-block-paragraph">Para simplificar el flujo podemos crear alias en <code>~/.zshrc</code>:</p>



<pre class="wp-block-code"><code>alias up-dev="brew services start mysql &amp;&amp; valet start"
alias down-dev="brew services stop mysql &amp;&amp; valet stop"</code></pre>



<p class="wp-block-paragraph">Después recargamos:</p>



<pre class="wp-block-code"><code>source ~/.zshrc</code></pre>



<p class="wp-block-paragraph">Y ahora podemos iniciar el entorno con:</p>



<pre class="wp-block-code"><code>up-dev</code></pre>



<p class="wp-block-paragraph">Y apagarlo con:</p>



<pre class="wp-block-code"><code>down-dev</code></pre>



<h2 class="wp-block-heading">Conclusión</h2>



<p class="wp-block-paragraph">Laravel Valet es una excelente alternativa para desarrollar proyectos Laravel en macOS. Comparado con MAMP, ofrece un flujo más limpio, rápido y cómodo para trabajar con dominios locales, PHP moderno, Composer, Node, Vite y MySQL.</p>



<p class="wp-block-paragraph">Aunque al inicio pueden aparecer algunos errores relacionados con PATH, Node, dependencias de npm o autenticación de MySQL, la mayoría tienen solución rápida si entendemos cómo está organizado el entorno.</p>



<p class="wp-block-paragraph">Para desarrolladores que trabajan constantemente con Laravel, una combinación como <strong>Laravel Valet + Homebrew + nvm + MySQL</strong> puede convertirse en una base de trabajo muy sólida y profesional.</p>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/laravel-valet-en-mac/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Agregar proyecto Laravel a GitHub</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/agregar-proyecto-laravel-a-github/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/agregar-proyecto-laravel-a-github/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Mon, 18 Nov 2024 05:39:33 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<category><![CDATA[Git]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[laravel]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=212</guid>

					<description><![CDATA[Carga tu proyecto de Laravel a un repositorio de GitHub por medio de la terminal de comandos...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Para este artículo vamos a suponer que ya tienes un proyecto de <em><strong>Laravel</strong></em> funcionando y lo quieres integrar a un repositorio ya se público o privado.</p>



<h3 class="wp-block-heading">GitHub</h3>



<p class="wp-block-paragraph">Al crear el repositorio nosotros te recomendamos que lo dejes vacío ya que esto evitará que tengamos que hacer un <em><strong>commit</strong></em> extra al momento de sincronizar el proyecto.<br>Ignora estos documentos a menos que los necesites:</p>



<ul class="wp-block-list">
<li>Read me.</li>



<li>Gitignore.</li>



<li>Licence.</li>
</ul>



<h3 class="wp-block-heading">Inicializar proyecto</h3>



<p class="wp-block-paragraph">Desde la terminal (consola) ingresamos a nuestra carpeta.</p>



<pre class="wp-block-code"><code>cd /ruta/denuestra/carpeta</code></pre>



<p class="wp-block-paragraph">Una vez estando en nuestra carpeta ejecutamos los siguientes comandos:</p>



<ol class="wp-block-list">
<li>git init</li>



<li>git add .</li>



<li>git commit -am &#8220;Inicialización del proyecto&#8221;</li>
</ol>



<p class="wp-block-paragraph">Una vez que ya tenemos inicializado nuestro proyecto procedemos a sincronizar la información, los siguientes comandos te los da el mismo GitHub al momento de crear el repositorio vacío.</p>



<pre class="wp-block-code"><code>git remote add origin git@github.com:tusuario/turepositorio.git
git branch -M main
git push -u origin main</code></pre>



<p class="wp-block-paragraph">Si todo ha ido bien ya podrás ver tu proyecto sincronizada con tu repositorio.</p>



<p class="wp-block-paragraph">Si necesitas ayuda para crear un proyecto de software en <strong>Maria Tech </strong>contamos con los expertos para apoyarte.</p>



<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-794e3cfa wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://wa.me/523414305984" target="_blank" rel="noreferrer noopener">(+52) 341-430-59-84</a></div>
</div>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow">
<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="mailto:info@mariatech.com.mx" target="_blank" rel="noreferrer noopener">info@mariatech.com.mx</a></div>
</div>
</div>
</div>



<p class="wp-block-paragraph"></p>
]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/agregar-proyecto-laravel-a-github/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Restringir el acceso de usuarios a uno por dispositivo en Laravel</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/restringir-el-acceso-de-usuarios-a-uno-por-dispositivo-en-laravel/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/restringir-el-acceso-de-usuarios-a-uno-por-dispositivo-en-laravel/#comments</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Tue, 28 May 2024 20:05:02 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[laravel]]></category>
		<category><![CDATA[autentificación]]></category>
		<category><![CDATA[seguridad]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=168</guid>

					<description><![CDATA[Aplica la función "logoutOtherDevices" para restringir el acceso de los usuarios a uno por dispositivo...]]></description>
										<content:encoded><![CDATA[<p>Cuando trabajamos con el <em>Framework </em>de <em>Laravel</em> y la autentificación de usuarios, por default las sesiones de los usuarios se quedan abiertas en todos los dispositivos(lo que dura el tiempo de vida la sesión).<br />En algunas ocasiones es necesario restringir ese acceso a que sea una única sesión abierta por usuario en un solo equipo, para resolver esto podemos usar la función &#8220;<strong>logoutOtherDevices</strong>&#8221; que viene incluida desde la versión 5.6 de <em>Laravel.</em></p>
<p><strong>logoutOtherDevices:</strong> Invalida la sesión del usuario en otro dispositivos, lo saca de la cuenta y solo mantiene la sesión actual del dispositivo que se está utilizando.</p>
<p>1- Integrar el <em>middleware </em>&#8220;<strong>auth.session</strong>&#8221; a la ruta o grupo de rutas donde se requiere tener acceso a la aplicación</p>


<pre class="wp-block-code"><code>Route::middleware(&#91;'auth',<strong>'</strong>auth.session'])->group(function () {
//Aquí van todas tus rutas
}</code></pre>


<p>2- Verificar que tenemos activo el <em>middleware </em>de &#8220;<strong>auth.session</strong>&#8220;<br />Para eso nos iremos a nuestro archivo &#8220;<strong>/app/http/kernel.php</strong>&#8220;, y asegurarnos que el arreglo &#8220;<strong>$middlewareAliases</strong>&#8221; tenga los valores:</p>


<pre class="wp-block-code"><code>protected $middlewareAliases = &#91;
'auth.session' =&gt; \Illuminate\Session\Middleware\AuthenticateSession::class,
];</code></pre>


<p>Nota: Debes tener más valores en el array, aquí solo te mostramos del que estamos hablando.</p>
<p>3- Agregar la función para ejecutar el proceso de cerrar las otras sesiones<br />Esto lo haremos en el controlador de autentificación &#8220;<strong>/app/Http/Controllers/Auth/AuthenticatedSessionContoller.php</strong>&#8220;,  agregamos la función &#8220;<strong>authenticated</strong>&#8221; (pueden usar otro nombre para la función)</p>


<pre class="wp-block-code"><code>/**
* authenticated Logout other devices
* @param string $current_password
*/
protected function authenticated($current_password) {
  Auth::logoutOtherDevices($current_password);
}</code></pre>


<p>4- Incluir la llamada de la función cuando el usuario ya se autentificó en la aplicación</p>


<pre class="wp-block-code"><code>/**
* Handle an incoming authentication request.
*/
public function store(LoginRequest $request): RedirectResponse
{
  $request-&gt;authenticate();
  $request-&gt;session()-&gt;regenerate();
  $this-&gt;authenticated($request-&gt;password);
  return redirect()-&gt;intended(RouteServiceProvider::HOME);
}</code></pre>


<p>5- Limpiamos la cache de las rutas para que nos detecte la aplicación las modificaciones</p>


<pre class="wp-block-code"><code>php artisan route:cache</code></pre>


<p>Referencias</p>


<p class="wp-block-paragraph"><a href="https://laravel.com/docs/10.x/authentication#invalidating-sessions-on-other-devices" target="_blank" rel="noreferrer noopener">https://laravel.com/docs/10.x/authentication#invalidating-sessions-on-other-devices</a></p>



<p class="wp-block-paragraph"><a href="https://laracasts.com/discuss/channels/laravel/logout-users-from-other-devices" target="_blank" rel="noreferrer noopener">https://laracasts.com/discuss/channels/laravel/logout-users-from-other-devices</a></p>


<p>Si te ayudó este contenido no olvides dejarnos tu comentario, bye.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/restringir-el-acceso-de-usuarios-a-uno-por-dispositivo-en-laravel/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title>Enviar correos con Gmail y PHPMailer</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/enviar-correos-con-gmail-y-phpmailer/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/enviar-correos-con-gmail-y-phpmailer/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Mon, 20 May 2024 16:18:06 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=152</guid>

					<description><![CDATA[Envía correos utilizando la librería de PHPMailer y tu cuenta de correo electrónico de Gmail..]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Si tienes una aplicación web, tienda en línea o página web dónde se necesita enviar correos, puedes hacer uso de la librería <em>PHPMailer</em>.<br>En caso de no tener un correo con dominio propio puedes utilizar tu cuenta de <em>Gmail</em>.</p>



<p class="wp-block-paragraph"><em>PHPMailer</em> es una librería basada en <em>PHP</em> que nos permite enviar y recibir correos usando el protocolo <em>SMTP</em>.<br>Para poder hacer uso de ella debemos integrarla a nuestros proyecto, aquí te dejamos el link para que veas cómo se hace y le des una leída a la documentación:</p>



<p class="wp-block-paragraph"><a href="https://github.com/PHPMailer/PHPMailer" target="_blank" rel="noreferrer noopener">https://github.com/PHPMailer/PHPMailer</a></p>


<p>Antes de configurar nuestras credenciales debemos asegurarnos de dos cosas:<br />1- Tener activada la extensión <em>openssl</em> en nuestro servidor, si no la tenemos nos dirigimos al archivo de <em>php.ini</em> y descomentamos la línea</p>


<pre class="wp-block-code"><code>extension=php_openssl.dll</code></pre>


<p>2- Activar el acceso a aplicaciones poco seguras de <em>Gmail,</em> te dejamos el link de cómo hacerlo:</p>


<p class="wp-block-paragraph"><a href="https://support.google.com/a/answer/6260879" target="_blank" rel="noreferrer noopener">https://support.google.com/a/answer/6260879</a></p>


<p>Una vez que tengamos incluido <em>PHPMailer</em> en nuestro proyecto, tengamos activa la extensión de <em>openssl</em> y activado el acceso a aplicaciones poco seguras de <em>Gmail</em> esta sería la configuración de las credenciales:</p>


<pre class="wp-block-code"><code>$config = array (
  'params' =&gt; 
  array (
    'mailer' =&gt; 'smtp',
    'secure' =&gt; 'ssl', 
    'host' =&gt; 'smtp.gmail.com',
    'port' =&gt; 465,
    'auth' =&gt; true,
    'user_name' =&gt; 'user_name@gmail.com',
    'password' =&gt; 'password',
    'from_name' =&gt; 'User Name',
    'debug' =&gt; 0
  )
);</code></pre>


<p>Te recomiendo que leas la documentación de <em>PHPMailer</em> antes de comenzar a usarla para te sea más sencillo aplicarla en tu proyecto y recuerda que si te ayudó este contenido déjanos tu comentario, bye.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/enviar-correos-con-gmail-y-phpmailer/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Convertir número a letras en Laravel</title>
		<link>https://www.mariatech.com.mx/blog/laravel-php/convertir-numero-a-letras-en-laravel/</link>
					<comments>https://www.mariatech.com.mx/blog/laravel-php/convertir-numero-a-letras-en-laravel/#respond</comments>
		
		<dc:creator><![CDATA[Victor Santillan]]></dc:creator>
		<pubDate>Thu, 09 May 2024 03:53:25 +0000</pubDate>
				<category><![CDATA[Laravel / PHP]]></category>
		<guid isPermaLink="false">https://www.mariatech.com.mx/blog/?p=129</guid>

					<description><![CDATA[Si estás buscando convertir un número a letras en Laravel nosotros te recomendamos usar la librería de Lecano...]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Si estás buscando convertir un número a letras en Laravel nosotros te recomendamos usar la librería de Lecano, funciona muy bien, nosotros estamos usando la versión 10 de Laravel.</p>



<p class="wp-block-paragraph">La librería la encuentras en Packgist bajo el nombre &#8220;luecano/numero-a-letras&#8221; aquí te dejamos el link:</p>



<p class="wp-block-paragraph"><a href="https://packagist.org/packages/luecano/numero-a-letras">https://packagist.org/packages/luecano/numero-a-letras</a></p>



<p class="wp-block-paragraph">En este mis enlace te explica de una manera clara y precisa cómo agregarlo a tu proyecto y cómo usarlo.</p>



<p class="wp-block-paragraph">De igual también lo encuentras en GitHub por si quisieras usarlo en otros proyectos que no fueron desarrollados con Laravel.</p>



<p class="wp-block-paragraph"><a href="https://github.com/lecano/php-numero-a-letras.git">https://github.com/lecano/php-numero-a-letras.git</a></p>


<p>Aquí te dejamos un pequeño ejemplo para te des una idea como funciona</p>


<pre class="wp-block-code"><code>use NumeroALetras;
class NumeroALetrasController extends Controller {
 public function getNumeroALetra(float $numero) {
    $formatter = new NumeroALetras();
    $formatter->apocope = true;
    $formatter->conector = "pesos";
    $amount_text = ucfirst(Str::lower($formatter->toInvoice($numero, 2)));
    $amount_text .= "M.N.";
    return $amount_text;
    }
}</code></pre>


<p>Suponiendo que el número que nosotros pasamos a la función es $58,518.72 el valor de retorno sería el siguiente: <strong>Cincuenta y ocho mil quinientos dieciocho pesos 72/100 M.N.</strong></p>

<p>Lee la documentación para que puedas sacarle todo el proyecto a la librería. </p>
<p>Y recuerda que si te sirve este contenido no te olvides de dejarnos un comentario bye.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mariatech.com.mx/blog/laravel-php/convertir-numero-a-letras-en-laravel/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
