RogerManich liked a comment+100 XP
1w ago
Hi @rogermanich You can take trait + lifecyle hook approach like this
https://gist.github.com/ghabriel25/6f33b8f8d8ea7306681761267c1d9ea4
Note you can create dedicated livewire component for this and put it in your layout
// in your layout
<livewire:toast />
Route::post('/lab', function () {
return back()
->with('flux:success', 'Record saved successfully!');
})->name('lab')->middleware('auth');
...
trait WithToast
{
public function renderedWithToast(): void
{
// check if there are flash session
// use Flux::toast to show it
// specify variant based on session key
// For example
if (Session::has('flux:success')) {
Flux::toast(text: Session::pull('flux:success'), variant: 'success');
}
}
}
then you can use this trait in your component. Also, you can create a trait specially for the toast session key, so that it becomes one source.
trait WithSessionKey
{
public const string TOAST_SUCCESS = 'flux:success';
}
...
class LabController extends Controller
{
use WithSessionKey;
public function update()
{
...
return back()
->with(self::TOAST_SUCCESS, 'Record saved successfully!');
}
}
RogerManich wrote a reply+100 XP
3w ago
Finally I found a possible solution that avoid flickr and no warnings errors on debug console.
import { createInertiaApp } from '@inertiajs/vue3';
import { initializeTheme } from '@/composables/useAppearance';
import AppLayout from '@/layouts/AppLayout.vue';
import AuthLayout from '@/layouts/AuthLayout.vue';
import SettingsLayout from '@/layouts/settings/Layout.vue';
import { initializeFlashToast } from '@/lib/flashToast';
//lang support
import { createSSRApp, h } from 'vue';
import { i18nVue, loadLanguageAsync } from 'laravel-vue-i18n';
const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
createInertiaApp({
title: (title) => (title ? `${title} - ${appName}` : appName),
layout: (name) => {
switch (true) {
case name === 'Welcome':
return null;
case name.startsWith('auth/'):
return AuthLayout;
case name.startsWith('settings/'):
return [AppLayout, SettingsLayout];
default:
return AppLayout;
}
},
/*
setup({ el, App, props, plugin }) {
const app = createApp({ render: () => h(App, props) })
.use(plugin)
.use(i18nVue, {
lang: (props.initialPage.props.locale as string) ?? 'en',
fallbackLang: 'en',
resolve: async (lang: string) => {
const langs = import.meta.glob('../../lang/*.json');
return await langs[`../../lang/${lang}.json`]();
},
});
// Only mount in the browser. On SSR `el` is undefined and mounting
// would try to access `window`.
if (typeof window !== 'undefined' && el) {
app.mount(el);
}
return app;
},
*/
setup({ el, App, props, plugin }) {
const langModules = import.meta.glob<{ default: Record<string, string> }>(
'../../lang/*.json',
{ eager: true },
);
const currentLocale = (props.initialPage.props as { locale: string }).locale;
const app = createSSRApp({ render: () => h(App, props) })
.use(plugin)
.use(i18nVue, {
lang: currentLocale,
fallbackLang: 'en',
resolve: (lang: string) =>
langModules[`../../lang/${lang}.json`]?.default ?? {},
});
if (typeof window !== 'undefined' && el) {
// Ensure translations are fully resolved before hydration
// starts, so server-rendered HTML matches client output.
loadLanguageAsync(currentLocale).then(() => app.mount(el));
}
return app;
},
progress: {
color: '#4B5563',
},
});
// This will set light / dark mode on page load...
initializeTheme();
// This will listen for flash toast data from the server...
initializeFlashToast();
RogerManich started a new conversation+100 XP
3w ago
Hi,
I am in learning process and I started a new project to practice a bit. My first goal is having language support to my App using vue starter kit.
The backend part is acomplished and the frontend almost acomplished there is a refresh issue. When I refresh the page I see the original labels and then the translated ones. This is weird for me. But first I want to review what I did in case I did somenthing wrong. Once I create standard project with Larave 13 + inertia + vue3 + authorization enabled:
- populate language files
php artisan lang:publish
- create en.json in lang folder with my labels.
- translate al language files to my supported languages (somthing I'll do at the end. For the moment. Just create a few keys tot test it.)
- Add new field in users to store desired languge (in fact it is a json field named preferences with multiple options.)
- Create a new middleware SetLocal to get current language in Laravel
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\App;
class SetLocale
{
public function handle(Request $request, Closure $next)
{
$available = array_keys(config('app.available_locales'));
// Precendece
// 1. session locale
// 2. user
// 3. Browser preferences
// 4. config.
$locale = $request->session()->get('locale')
?? optional($request->user())?->lang
?? $request->getPreferredLanguage($available)
?? config('app.locale', 'en');
if (in_array($locale, $available, true)) {
App::setLocale($locale);
} else {
//Misconfiguration issue => en
App::setLocale('en');
}
return $next($request);
}
}
- Register middleware in bootstrap app.php
<?php
use App\Http\Middleware\HandleAppearance;
use App\Http\Middleware\HandleInertiaRequests;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;
use App\Http\Middleware\SetLocale;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->encryptCookies(except: ['appearance', 'sidebar_state']);
$middleware->web(append: [
HandleAppearance::class,
SetLocale::class, /* THIS */
HandleInertiaRequests::class,
AddLinkHeadersForPreloadedAssets::class,
]);
})
->withExceptions(function (Exceptions $exceptions): void {
//
})->create();
- add in shere method in HandleInertiaRequests two data to be shered with frontend. current language and available language (for future language selector not covered here.).
public function share(Request $request): array
{
return [
...parent::share($request),
'auth' => [
'user' => $request->user(),
],
'locale' => fn () => app()->getLocale(),
'available_locales' => fn () => config('app.available_locales'),
];
}
- Install laravel-vue-i18n
- Add in vite.config.js
//at the beginning of the file.
import i18n from 'laravel-vue-i18n/vite';
//in plugins section
i18n(),
- In app ts
//at the beginning of file
import { createApp, h } from 'vue';
import { i18nVue } from 'laravel-vue-i18n';
//Inside the createInertiaApp
setup({ el, App, props, plugin }) {
const app = createApp({ render: () => h(App, props) })
.use(plugin)
.use(i18nVue, {
lang: (props.initialPage.props.locale as string) ?? 'en',
fallbackLang: 'en',
resolve: async (lang: string) => {
const langs = import.meta.glob('../../lang/*.json');
return await langs[`../../lang/${lang}.json`]();
},
});
// Only mount in the browser. On SSR `el` is undefined and mounting
// would try to access `window`.
if (typeof window !== 'undefined' && el) {
app.mount(el);
}
return app;
},
- Now we can use this to translate
in php
__('hello world!');
In vue3
{{ $t('hello world') }}
And now my question:
It works fine. But when I refresh the page I see the original label "hello world" and then the translated text "Hola món". Is there something wrong? Maybe my vue part is not quite fine and I forget something or I doing something too late?
thank you
RogerManich liked a comment+100 XP
3w ago
@Ottenim I google the question and I check documentation.
What you said is right but I want to display i in different languages. (Current app Language). For this case we can use:
php artisan lang:publish
To populate language files. Then make copies for each language you want to support. en, fr, sk, ca, es, nl, etc.
Finally, you can use it in your backend with __('hello world'). It also accept placeholders. If you use Livewire it also works fine. For vue3 I am investigating a bit. Acomplished but I need to polish some things. Ah, don't forget to add a middleware SetLocale to obtain your current language based on your criteria and precedence (session => user => browser => config => hardcoded default.
RogerManich wrote a comment+100 XP
3w ago
@Ottenim I google the question and I check documentation.
What you said is right but I want to display i in different languages. (Current app Language). For this case we can use:
php artisan lang:publish
To populate language files. Then make copies for each language you want to support. en, fr, sk, ca, es, nl, etc.
Finally, you can use it in your backend with __('hello world'). It also accept placeholders. If you use Livewire it also works fine. For vue3 I am investigating a bit. Acomplished but I need to polish some things. Ah, don't forget to add a middleware SetLocale to obtain your current language based on your criteria and precedence (session => user => browser => config => hardcoded default.
RogerManich wrote a reply+100 XP
1mo ago
It what I did but it didn't work it. Looks like I need to do something like this:
Route::get('/lab', function () {
return back()->with('flux-toast', [
'text' => 'hellou',
'variant' => 'success',
]);
})->name('lab')->middleware('auth');
Which it is pretty much the same as session()->flash. Maybe there is something easier we can do. But as @imranbru suggest, not bad solution using this approach.
For the moment, with both works fine. Only one is displayed.
RogerManich wrote a reply+100 XP
1mo ago
thank you, I will
RogerManich started a new conversation+100 XP
1mo ago
Hi,
After I watched the last Laravel course I wanted to try to build something with Laravel + LiveWire. My first goal was expanding the user preferences to accept language and color scheme. It works but in my first design I wanted to use a route + controller to update the user preferences instead of doing it insde the component settings. It worked but I realized that flux::toast doesn't work in this scenario. I've seen a post where explaining that now is supported out of the box. Well, inside the component works fine.
After googling a bit and some IA help I found out that I require to use session to store the message and add some listener in my layout. Now it works but I wonder if I did something wrong and I am reinventing the wheel or the starterkit missing this piece of code.
thank you
a Route example:
Route::post('/lab', function () {
session()->flash('flux-toast', [
'text' => 'Record saved successfully!',
'variant' => 'success',
]);
return redirect()->back();
})->name('lab')->middleware('auth');
and in my layout after
@persist('toast')
<flux:toast position="top right" />
@endpersist
@if(session()->has('flux-toast'))
@php $toast = session('flux-toast'); @endphp
<script>
function showToast() {
Flux.toast({
text: '{{ $toast['text'] }}',
variant: '{{ $toast['variant'] ?? 'success' }}',
});
}
if (document.readyState === 'complete') {
showToast();
} else {
document.addEventListener('DOMContentLoaded', showToast, { once: true });
}
document.addEventListener('livewire:navigated', showToast, { once: true });
</script>
@endif
RogerManich liked a comment+100 XP
1mo ago
@RogerManich Yes, I noticed this after a while. Thanks Roger
RogerManich wrote a comment+100 XP
1mo ago
@Go3shom the if is correct but did you fix it in the when clausule?
require to be $status instead of $request->status
$ideas = $user
->ideas()
->when($status, fn($query, $status) => $query->where('status', $status))
->get();
RogerManich liked a comment+100 XP
1mo ago
@wordxpression If I understand what you mean right, you may get the All button count by calling the count method on $statusCounts like so:
<span class="text-xs pl-3">{{ $statusCounts->count() }}</span>
RogerManich liked a comment+100 XP
1mo ago
As much as I like to practice array mapping and manipulation, countBy() helps to clean the logic up a bit:
// Get counts in the controller
'counts' => $request->user()->ideas()->get()->countBy('status');
// Get counts in view, including those with no records
{{ $counts[$ideaStatus->value] ?? 0 }}
RogerManich wrote a comment+100 XP
1mo ago
@RogerManich a better version
@props([
'status' => ""
])
@php
use App\IdeaStatus;
$classes = 'inline-block rounded-full border px-2 py-1 text-xs font-medium';
$classes .= match($status) {
IdeaStatus::PENDING->value => ' bg-yellow-500/10 text-yellow-500 border-yellow-500/20',
IdeaStatus::IN_PROGRESS->value => ' bg-blue-500/10 text-blue-500 border-blue-500/20',
IdeaStatus::COMPLETED->value => ' bg-green-500/10 text-green-500 border-green-500/20',
default => ' bg-red-500/10 text-red-500 border-red-500/20'
};
@endphp
<span {{ $attributes(['class' => $classes]) }}>
{{ $slot }}
</span>
RogerManich wrote a comment+100 XP
1mo ago
I wonder if it is better use this code. Use a switch instead of several if statements. Use the IdeaStatus enum we created in previous lessons to have consistency. Adding a red color for unknown status (improvably but possible). It is what I do in real life but not sure if Laravel has any considerations to use strings or it is just a lesson and Jeffrey make easier to use strings to not add to much stuff.
@props([
'status' => null
])
@php
use App\IdeaStatus;
$classes = 'inline-block rounded-full border px-2 py-1 text-xs font-medium';
switch($status) {
case IdeaStatus::PENDING->value:
$classes .=' bg-yellow-500/10 text-yellow-500 border-yellow-500/20';
break;
case IdeaStatus::IN_PROGRESS->value:
$classes .=' bg-blue-500/10 text-blue-500 border-blue-500/20';
break;
case IdeaStatus::COMPLETED->value:
$classes .=' bg-green-500/10 text-green-500 border-green-500/20';
break;
default:
$classes .=' bg-red-500/10 text-red-500 border-red-500/20';
}
@endphp
<span {{ $attributes(['class' => $classes]) }}>
{{ $slot }}
</span>
RogerManich wrote a comment+100 XP
1mo ago
@senxor it's a matter of trying and check if it works. Or do you asking if it is a good practice?
RogerManich wrote a comment+100 XP
2mos ago
RogerManich liked a comment+100 XP
2mos ago
I’m using https://heroicons.com/, and it has worked well for me. I just copy the icon, create the Blade view, and add the width and height attributes set to 20, but that’s optional. Thank you
RogerManich wrote a comment+100 XP
2mos ago
@XavierHsiao it is explained in next episodes.
RogerManich liked a comment+100 XP
2mos ago
Seems that Jeffery did not mention displaying flash data. We just need to add it from seesion as below:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Idea</title>
@vite(['resources/css/app.css'])
</head>
<body class="bg-background text-foreground">
<x-layout.nav />
{{-- Flash Data --}}
@if (session('success'))
<div class="fixed bottom-4 right-4 bg-primary text-primary-foreground px-6 py-3 rounded-xl shadow-lg">
{{ session('success') }}</div>
@endif
<main class="max-w-7xl mx-auto px-6 pb-10">
{{ $slot }}
</main>
</body>
</html>
RogerManich wrote a comment+100 XP
2mos ago
@Alext1926 we use the DB each time we use Auth::login or Auth::destroy, Auth::attempt, etc. You are using a builtin Laravel classes that make this tasks out of the box. If you open your database you'll see the registered users and sessions.
Maybe did I misunderstand your question?
RogerManich wrote a comment+100 XP
2mos ago
Hi, I have a little question: When we logout, the session record remains in the database without the user_id. Is this a security measure to avoid reusing session id or we should destroy it?
RogerManich wrote a comment+100 XP
2mos ago
@hendriaprianto99 well, maybe you have something in mind.
RogerManich wrote a comment+100 XP
2mos ago
Just a question @Jefrey: In real life when you work with git, you only maintain one branch? Main / develop branch? Main / Develop and one branch per feature you work?
thanks
RogerManich wrote a comment+100 XP
2mos ago
@nosthas you added WithNames.
What I see this rector.php modify some original file in Laravel. Not sure if it is good practice do that.
RogerManich wrote a comment+100 XP
2mos ago
@crscnc if you plan increase complexity there are a few great course in here: Laravel API masterclass, Modular Laravel and others. My concern is about time to dedicate to a technology: Blade looks like great but when you plan to do things close to app maybe vue3 is better. But not entirely sure. Maybe you dedicate efforts to vue3 and an experienced developers tell you that now the trend is CanavisReact 4 (fake name) because it handle better some things that vue3 doesn't. As an example: I usually use MariaDB for all kind of projects. It is a bit work to configure MariaDB, maybe a Linux, Apache or nginx, etc. @jeffreyway suggest in a course that SQLite should be enough for most Laravel projects. I tried it and looks like a good advice. So, I expect the opinion who was there in the battle. You know. :-)
Thank you
RogerManich wrote a comment+100 XP
2mos ago
@crscnc The point is about choosing the right technology among all that Laravel offers. As an example: Before starting to follow Jeffrey I use to make my little projects with MariaDB. Jeffrey suggest in his Laravel in 30 days course that sqlite is good enough for little / medium projects. Good advice. I test it and it is ok. So, if I have to learn Blade or vue3 I want to know if at the end is better to use Vue or Blade is ok. Vue looks like better in terms of I can use it in other places, for instance. Just want to leverage the experience of who were there before.
RogerManich wrote a comment+100 XP
2mos ago
@jawaid interface is something you define independent of a class. If an interface define get, set, erase methods all the classes that implements that interface must define that methods. A class can accept multiple interfaces.
When you use an abstract class is more specifically to think with a classes that you want to instruct how you want to be defined. It can includes implementation (I think it. not sure). It is like a sketch of your class in terms of methods, and specific implementations.
More less.
RogerManich wrote a comment+100 XP
2mos ago
I knew this as polimorphism that is used in other languages. When I realized I can use interface to achieve that I was so glad. But I didn't know the concept of contract which is great too. Thanks for this masterclass.
RogerManich liked a comment+100 XP
2mos ago
Even as a long-time PHP developer I find this series incredibly helpful and approachable! Really great job, Jeffrey!
RogerManich liked a comment+100 XP
2mos ago
And in the controller if we are using authorize method with one parameter, we are using gate:
Gate::authorize(parameter1);
// ex.
Gate::authorize(gateName);
If we are using authorize method with 2 parameters, we are using policies:
Gate::authorize(parameter1, parameter2);
// ex.
Gate::authorize(methodName, modelOrClassInstance);
RogerManich wrote a comment+100 XP
2mos ago
@akaydin I think if you plan having undetermined amount of admins and don't want to make anything complex, you can replicate what Jeffrey suggest: A field named role and define different roles such as user, advanced user, operator, admin, superadmin, etc. According to your needs.
if you require anything more sophisticated you can create a permissions table, role table and pivot table. Then you can associate that role to a user. Each permission can correspond to a Gate. And inside the gate you have to check if you have that particular permission.
RogerManich wrote a comment+100 XP
3mos ago
On login, in case of bad credentials we can use a special name to show general errors
return back()->withErrors([
'general' => 'The provided credentials do not match our records.',
]);
}
and then in our view:
<SNIP>
<x-forms.error name="general" />
</fieldset>
<SNIP>
RogerManich wrote a comment+100 XP
3mos ago
@jake83 I thought the same. I test it in my project and update is required to update the data which is what I expect.
RogerManich wrote a comment+100 XP
3mos ago
@jeffry if we want to display the messages in a different language is there a way to achieve that?
RogerManich wrote a comment+100 XP
3mos ago
thanks for the trick about pushing a button and fire a different form. I didn't remeber that. :-)
RogerManich liked a comment+100 XP
3mos ago
@koulritesh98 While developing, you added some new tables or columns to existing tables, after you deployed to production. You add them by writing new migrations. Then, when you update your production site, you also update your table by running "php artisan migrate". It will ask you "are you sure, this is prod?", you answer yes, and the prod DB is updated. It will run only the newly added migrations, not the old ones. How, might you ask? It keeps a list of migrations that have been already run in the DB table called "migrations". Open it and see for yourself.
Of course, it goes without saying: always double check what your migrations are doing and always made a DB backup before updating.
RogerManich liked a comment+100 XP
3mos ago
@jeffreyway in every video, there's a moment cracks me up. This time, the super explanation for the 419 page expired.. typing fast enough ahahah.
RogerManich wrote a comment+100 XP
3mos ago
@subhrodeb888 I think he meant that when you define a get route is to fetch data and ultimatelly display / return something. It is not a good practice to do something different there like delete something. If you plan to delete something you need to define a route for this. Rest define a way to do this.
RogerManich wrote a comment+100 XP
3mos ago
I like this new course: You combine the video and you communication capability with some text-based exampled to help us to put in motion. Great!
RogerManich wrote a comment+100 XP
3mos ago
As we can work with Blade, Vue or React what do you recommend to use in Laravel @jeffreyway? Blade is enough or is limited and Vue is much powerful? I have a bit confusion how to use Vue in a project with Laravel (the Vue3 course is not clear enough in this part). But finally I see it and I wonder what is the best? Or when to choose one or another based on your experience.
RogerManich wrote a comment+100 XP
3mos ago
@timgavin Yes and No. From my point of view, places like Laracast are escencial because the course and the teacher is like the man that show the right path. Show you the best practices. AI without knowlede and context is often dangerous. To solve a problem through an AI you need to know what are the right questions. You need to know the capabilities of the technology you choose. And here is where Laracast is valuable. Once you draw the map thanks to the course is when you can start to use AI to expand your knowledge or to help you to build something. Or you can use to clarify something is not entirely clear in the course. But without this essencial context AI is like a car without keys.
RogerManich wrote a comment+100 XP
3mos ago
What I miss in this setup is a 30 seconds to indicate what extensions required to work on VS Code. Php Storm is not free. Only 30 days which is not much.
Also it could be nice some references to Linux. Not everything is apple.
At the end, it is a couple of lines to have an operational setup.
Anyway, you contents are pretty amazing!
thank you Roger