iamGus liked a comment+100 XP
1mo ago
This is pretty disappointing for being part of a paid Laravel course to be honest.
Obviously frontend stuff is unavoidable and I appreciate the initial intention behind bringing AlpineJS in, but it has led to this, an entire episode of refactoring ugly rushed inline Javascript inside quotes with no linting or syntax highlighting, in a tutorial that's supposed to be about a full-stack PHP framework.
When you're teaching PHP & Laravel, you're amazing. But the next time you re-do this course, please do better on the frontend.
iamGus liked a comment+100 XP
1mo ago
I don’t like this lesson. It’s difficult, but not because the topic itself is complex — it’s because there are many errors in the code. As a student, I run into these issues and initially assume that I’ve misunderstood something. But after spending several hours debugging, making two attempts, and carefully watching the video to the end, I realized that the same issues exist in Jeffrey’s code — he’s just not as thorough with testing as I am.
This really slows me down and makes it harder to absorb the material. What’s especially frustrating is that this lesson contrasts sharply with the rest of Jeffrey’s courses, which are usually clear and well-explained.
iamGus liked a comment+100 XP
1mo ago
Indeed a very complex lecture, in my opinion due to the nature that most stuff is specific to "alpine.js". Apart from that it would really help a lot to use an IDE/editor where the filename is permanently visible.
iamGus liked a comment+100 XP
1mo ago
The episode was truly complex this time. I didn’t understand everything because I felt it could have been simpler but I’ll watch it several times. Thanks Jeffrey for your outstanding content (you're my favorite teacher), but this goes too far for a Laravel example – I think I'm going to take a triple coffee break, lol 🫠.
iamGus liked a comment+100 XP
1mo ago
Hello, I notice that when we removed the image, it closed the modal. Should we rather create a rest api that removes the image, and how would it look like in this flow?
iamGus liked a comment+100 XP
1mo ago
Thanks @marc_meat! 🙌🏻 After investigating, I confirmed that this is indeed the error. I found a possible workaround without modifying the Composer fork: dynamically setting the enctype with Alpine.js only when the user selects an image:
x-bind:enctype="hasImage ? 'multipart/form-data' : false"
This way, the form is submitted without multipart/form-data by default, and the browser test passes, but when the user selects an image, it changes automatically. I hope this helps!
iamGus liked a comment+100 XP
1mo ago
Hi just a quick question about security I may not be the only one wondering this so thought I'd ask. Anything in the public/ directory is public. So that means all featured images would be publically accessible providing you can guess the randomly generated URL. Is this covered in the Authorization is a requirement lesson where we ensure only the user can access their own idea images?
iamGus liked a comment+100 XP
1mo ago
In the controller, I modified the logic for storing an idea a little bit. I believe that in this case it is good practice to use database transactions because we depend on two queries working correctly: creating the Idea and creating each of its steps.
If one of them fails, the whole operation should be rolled back to keep the database consistent.
public function store(StoreIdeaRequest $request)
{
DB::transaction(function() use ($request): void {
$idea = Auth::user()->ideas()->create($request->safe()->except('steps'));
$steps = collect($request->safe()->input('steps', []))
->map(fn($step) => ['description' => $step])
->all();
if ($steps !== []) {
$idea->steps()->createMany($steps);
}
});
return to_route('idea.index')
->with('success', 'Idea created!');
}
iamGus liked a comment+100 XP
1mo ago
@rudysacostacrousset You can also wrap this inside of a try/catch block to flash an error response if needed.
public function store(StoreIdeaRequest $request): RedirectResponse
{
try {
DB::transaction(function () use ($request): void
{
$idea = Auth::user()->ideas()->create($request->safe()->except('steps'));
$idea->steps()->createMany(
collect($request->steps)->map(fn ($step) => ['description' => $step])
);
});
return to_route('idea.index')->with('success', 'Idea created!');
} catch (\Exception $e) {
// Show the error message.
return to_route('idea.index')->with('error', $e->getMessage());
// ...or show a more generic, user-friendly error message.
return to_route('idea.index')->with('error', 'Idea could not be created.');
}
}
iamGus wrote a comment+100 XP
1mo ago
Does someone know how to view the laravel documentation on the IDE (VSCode)? when hovering on a function, method or a reserved word, that would help me a lot
iamGus liked a comment+100 XP
1mo ago
I think you can try to use a more useful resource icons set like material icons that can be easely installed using Fontosource and give you a great collection of icons very ease to use. Thanks a lot Jeffrey!
iamGus liked a comment+100 XP
1mo ago
icons/close.blade.php
<svg {{ $attributes }} width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" role="img" aria-label="Close">
<path d="M18 6 6 18"></path>
<path d="m6 6 12 12"></path>
</svg>
iamGus liked a comment+100 XP
1mo ago
An easy way to add focus trap to the modal is to add this first party focus trap plugin: https://alpinejs.dev/plugins/focus
bootstrap.js
import axios from 'axios';
import Alpine from 'alpinejs'
import focus from '@alpinejs/focus'
window.axios = axios;
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
window.Alpine = Alpine;
Alpine.plugin(focus);
Alpine.start();
modal.blade.php
@props(['name', 'title'])
<div
x-data="{ show: false, name: @js($name) }"
x-show="show"
x-trap="show"
@open-modal.window="show = ($event.detail === name)"
@keydown.escape.window="show = false"
x-transition:enter="ease-out duration-250"
x-transition:enter-start="opacity-0 -translate-y-4"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-250"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0 -translate-y-4"
class="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-xs"
style="display:none;"
role="dialog"
aria-modal="true"
aria-labelledby="modal-{{ $name }}-title"
:aria-hidden="!show"
tabindex="-1"
id="modal-{{ $name }}"
>
<x-card is="div" @click.away="show = false">
<div>
<h2 id="modal-{{ $name }}-title" class="text-2xl font-bold">{{ $title }}</h2>
</div>
<div>
{{ $slot }}
</div>
</x-card>
</div>
And you can add some extra spice by disabling scrolling when a modal is open:
body:not(:has([role='dialog'][aria-hidden])) {
overflow: hidden;
}
iamGus liked a comment+100 XP
1mo ago
arrow-back.blade.php
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-arrow-left-icon lucide-arrow-left">
<path d="m12 19-7-7 7-7" />
<path d="M19 12H5" />
</svg>
iamGus liked a comment+100 XP
1mo ago
external.blade.php
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
class="lucide lucide-external-link-icon lucide-external-link">
<path d="M15 3h6v6" />
<path d="M10 14 21 3" />
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
</svg>
iamGus wrote a comment+100 XP
1mo ago
@kuremkarmerurk Thanks! I added the next class to show the logo well
class="h-8 w-auto"
iamGus liked a comment+100 XP
1mo ago
@rudysacostacrousset <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1164 409"><path d="M242.974 1.415c-6.2 0-13.201.7-16.301 1.5-7.8 2.1-15.3 5.7-22.6 10.9-6.9 5-9.4 7.9-9.4 11 0 1.4 6.4 8.4 18.8 20.7 15.9 15.8 19.8 19.1 26 22.2 15.2 7.6 32.3 8.5 48.9 2.6 14.1-5.1 16.5-5.5 21.9-4 2.7.8 6.5 2.9 8.7 4.9 5.6 5 7.7 9.8 7.7 17.4 0 9.6-3.1 13.9-30.8 41.9-27.5 27.9-28.8 29.4-33.2 36.8-14.7 24.8-14.4 57.3.6 80.7 5.2 8.2 11.3 14.3 14.1 14.3 3.5 0 83.8-80.2 88.2-88.1 10.5-19 12.4-41.1 5.2-60.9-5.7-15.5-10.4-21.1-52.3-62.2-21.4-20.9-40.9-39.2-44-41.2-9.6-6.1-18.9-8.6-31.5-8.5m-129.55 26.212c-2.725.037-4.7.139-5.25.289-17.9 4.5-23.6 8.7-57.5 42.499-30.6 30.5-36 36.8-42 49.3-12.6 26.3-7.7 59.2 11.8 79.8 6 6.3 6.8 5.9 24.3-12 20.8-21.1 25.6-29.5 28.1-48.8 1.4-10.6-.8-24.3-5.4-34.7-6.5-14.5-4.6-26.7 5.1-32.9 4.2-2.7 5.2-2.9 11.7-2.5 5.6.3 8.3 1 12 3.2 2.6 1.5 18.5 16.4 36.1 33.8 17.2 17.1 33.7 32.6 36.6 34.6 9.2 6.2 16.6 8.3 30.7 8.9 9.6.3 14.1 0 19-1.2 14.3-3.7 30-13.8 30-19.4 0-2.2-7.7-10.4-42.7-45.3-45.6-45.3-48.8-48-63.3-53-5.2-1.8-9.2-2.3-19.5-2.5-3.55-.1-7.025-.126-9.75-.088m445.25 55.687v45h44.9l.3-22.1c.2-12.1.1-22.3-.1-22.5s-10.5-.4-22.8-.4zm193 0v82.901l-5-5.3c-5.6-5.9-13.3-10.5-22.5-13.3-9-2.7-31.2-2.4-41.8.5-26.5 7.5-45.9 28.1-53.9 57.3-1.9 7.2-2.2 10.8-2.2 25.4 0 14.4.3 18.3 2.3 25.5 7.5 28.4 28.5 50.1 55.1 57 9.6 2.5 29.4 2.7 38.4.4 9.7-2.5 18.5-7.3 25.8-14.3l6.8-6.3v18.2h42v-228zm146.073 62.423c-5.429-.01-10.548.178-12.573.478-34.3 5.5-59.4 30.5-66 65.6-2.3 12.3-1.7 33.6 1.4 45.3 8 30.4 28.6 50.6 58 56.8 11.4 2.4 34 2.1 46.1-.5 24.2-5.2 44.1-21.4 52.5-42.8 1.2-3.2 2.5-7 2.9-8.6l.6-2.7h-42l-2.1 4.2c-12.1 23.7-53.5 23.2-67.6-.9-2.5-4.3-6.3-15.1-6.3-17.9 0-1.2 9-1.4 59.6-1.4h59.7l-.6-15.8c-.6-18.3-2.6-27.3-8.7-39.7-9.5-19.3-25.9-32.7-47.5-39-7.1-2.1-11.7-2.7-22-3a207 207 0 0 0-5.427-.078m174.382.024c-5.297-.065-10.243.103-13.655.553-15.5 1.9-32.5 9.701-41.8 19-8.4 8.5-15 22.7-15 32.7v3.3h41.8l1.3-3.799c3.7-11.6 14.1-18.2 28.4-18.2 17.7 0 28.5 10.3 28.5 27.2v4.8h-21.7c-30.9 0-41.2 1.8-57.2 9.6-45.6 22.4-39.7 79.8 9.5 92.4 10.7 2.8 29.1 2.8 40 0 10.7-2.7 21.5-8.3 28.2-14.7 3.1-2.9 5.7-5.1 5.8-5s1.1 2.1 2.3 4.4c2.7 5.5 9.4 10.8 16.6 13.3 6.7 2.3 26.4 2.8 34.1.9l4.4-1.1v-30.5l-5.1-.6c-2.8-.4-6.3-1.4-7.6-2.2-5-3.3-5.3-5.7-5.3-42.4 0-38.3-.6-43.4-6.6-55.9-7.2-15-24.5-27.7-43-31.7-5.312-1.188-15.117-1.946-23.945-2.054m-469.154 3.754c-.5.2-10.6.4-22.5.5l-21.802.3v161h45v-81c0-44.6-.298-80.9-.698-80.8m-479.416 6.861c-4.9-.05-13.584 9.173-42.984 38.639-41.5 41.7-45.101 46.1-50.701 61.5-2.5 6.7-2.7 8.5-2.7 22.3.1 13.5.3 15.8 2.7 22.8 4.8 14.2 9.4 20.5 29.2 40.3 25.4 25.3 53 50.9 59.9 55.6 13.5 9.2 24.7 12.2 42.7 11.5 10.1-.3 14-.9 20.5-3.1 14.5-4.9 28.3-14.6 28.5-20 0-2-33.7-34.9-40-39-17.9-11.7-38.2-13.8-60-6.3-6.4 2.2-12.4 3.7-15.3 3.7-12.9 0-24.2-12.4-22.3-24.5 1.4-8.7 4.3-12.3 34.1-42.5 27.7-28 29.4-30 33.4-38 12.2-24.8 8.3-54.6-10-76.8-2.922-3.54-4.449-6.112-7.015-6.139m777.218 23.092c6.161.067 13.059 1.246 17.496 3.246 11 5.1 17.901 14.302 20.801 27.802l.6 2.799h-76l.6-2.8c3.6-14.9 12.8-25.2 26.9-29.9 2.475-.825 5.907-1.187 9.603-1.147m-188.439 4.112a77 77 0 0 1 3.435.035c7.4.3 9.601.8 15.201 3.5 16.9 8.4 25.9 26.4 24.4 49.2-1.7 26.9-19.2 43.6-43.8 41.7-11.4-.9-17.9-3.8-25.3-11.1-6.7-6.6-9.6-11.7-12.1-21.1-1.8-6.9-2-20.2-.4-27.7 2.6-12.2 10.5-24.2 19.8-29.6 5.862-3.412 11.112-4.834 18.764-4.935m-342.064 30.735c-2.9 0-5.1 1.8-19.3 16.3-14.8 15-16.3 16.9-20.6 25.7-9.2 18.7-10 31.7-3.2 51.4 5.1 14.7 4.2 20.7-3.8 25.7-4.2 2.6-13.3 2.1-19.7-1.1-3-1.5-14.4-12.1-33.5-31-31.3-31.1-36.6-35.3-49.9-39.7-7.1-2.4-9.7-2.7-21.6-2.7-12.2-.1-14.3.2-22.2 2.8-8.6 2.8-23.1 11.3-25.1 14.7-.6.9-.7 2.7-.4 4 .4 1.4 19.3 21 43.9 45.5 45.9 45.7 47.3 46.9 62 52.1 8.6 3 26.5 4.2 36 2.4 8.5-1.5 20.3-6.6 27.2-11.5 7.2-5.1 54.8-53.1 59.5-60 4.8-6.9 8.8-16.1 11.2-25.6 4.1-16 1.4-37.1-6.7-53.3-5.4-10.7-9.8-15.7-13.8-15.7m724.09 29.083c7.467.061 8.31.418 8.31 1.618 0 4.7-2.3 15-4.1 18.5-3.1 6.1-10.3 12.6-17.5 16-12.3 5.8-29.5 4.9-36.8-1.8-7.4-7-7.4-18.6 0-25.6 3.6-3.3 11.1-6.7 17.3-7.8 2.5-.4 12.8-.801 22.9-.901 4.176-.025 7.4-.037 9.89-.017" style="fill:#fff;"/></svg>
iamGus liked a comment+100 XP
1mo ago
CodeRabbit is a paid service, and deserves every penny. But for a more laravel-native and free alternative, I'd recommend laravel-simplifier. php-simplifier's laravel-first version, kinda.
I commit and before pushing, I call Claude to scan with laravel-simplifier. Almost 99% accurate and most times I accept without touching anything. Then I push.
https://laravel-news.com/laravel-gets-a-claude-code-simplifier-plugin
iamGus liked a comment+100 XP
1mo ago
<?php
declare(strict_types=1);
use Rector\Config\RectorConfig;
use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnTypeFromStrictTypedCallRector;
use Rector\TypeDeclaration\Rector\ClassMethod\ReturnUnionTypeRector;
use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector;
use Rector\TypeDeclaration\Rector\StmtsAwareInterface\DeclareStrictTypesRector;
use RectorLaravel\Set\LaravelSetProvider;
return RectorConfig::configure()
->withPaths([
__DIR__.'/app',
__DIR__.'/bootstrap',
__DIR__.'/config',
__DIR__.'/public',
__DIR__.'/resources',
__DIR__.'/routes',
__DIR__.'/tests',
])
->withSkip([
__DIR__.'/bootstrap/cache',
__DIR__.'/storage',
__DIR__.'/vendor',
AddClosureVoidReturnTypeWhereNoReturnRector::class,
ReturnTypeFromStrictTypedCallRector::class,
ReturnUnionTypeRector::class,
DeclareStrictTypesRector::class => [
__DIR__.'/resources/views',
],
AddArrowFunctionReturnTypeRector::class,
])
->withPhpSets()
->withSetProviders(LaravelSetProvider::class)
->withImportNames()
->withComposerBased(laravel: true)
->withPreparedSets(
deadCode: true,
codeQuality: true,
typeDeclarations: true,
privatization: true,
earlyReturn: true,
)
->withRules([
DeclareStrictTypesRector::class,
]);
iamGus wrote a comment+100 XP
1mo ago
Here's a tip when using VSCode, Cursor or any other VSCode IDE based to format automatically when saving:
- Install the next extension: Laravel Pint by Open Southeners
- Press the shortcut
Cmd+Shift+Pand look forsettings.jsonor User Settings. - Add the next lines and save.
"editor.formatOnSave": true,
"editor.defaultFormatter": "open-southeners.laravel-pint",
- Ready, with that you don't need to run the comand to format the code, you just need to save. :)
iamGus liked a comment+100 XP
1mo ago
Learn more about being a good artisan.
iamGus liked a comment+100 XP
1mo ago
Amazing Jeffrey. Can you please create a course on REST API from start to end?
iamGus liked a comment+100 XP
1mo ago
I really like this series, A bit of comparison would be nice between the two as I'm still slightly confused what warrents a policy and what warrents using a normal gate. In this situation we would reach for a gate in this situtaion we would reach for a policy could you help me out a little :)
iamGus wrote a comment+100 XP
1mo ago
@Alext1926 couldn't find it too
iamGus wrote a comment+100 XP
1mo ago
@Bulat Auth::id() worked for me
iamGus liked a comment+100 XP
1mo ago
9:39 'user_id' => Auth::user()->id,
iamGus liked a comment+100 XP
1mo ago
One thing to add here the :attribute value in messages() has some hidden functionality. If you want the attribute name to have a capital letter use :Attribute if you want it to be full caps lock use :ATTRIBUTE. Really cool stuff...
iamGus liked a comment+100 XP
1mo ago
At 7:28 I assume you meant to remove a validation rule, however there isn't one currently in the update method. Instead you remove the update on the Idea model. Presumably this is a mistake? Great tutorial so far btw :)
iamGus liked a comment+100 XP
1mo ago
@Tray2 Why do you think that is? I am curious!
iamGus liked a comment+100 XP
1mo 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.
iamGus liked a comment+100 XP
1mo ago
@subhrodeb888 GET is supposed to be idempotent. This is stated in RFC 7231. So any data modification or destruction would be bad.
iamGus liked a comment+100 XP
1mo ago
prod quality so good
iamGus liked a comment+100 XP
1mo ago
This series is amazing.
iamGus wrote a comment+100 XP
1mo ago
@noah-hawkett Hi Noah, the difference is that you don't explicitly have the same information on all of your routes/pages, you have "dynamic" content when you browse through them