SpecBit wrote a comment+100 XP
1w ago
Hi Jeffrey,
I just finished the Laravel From Scratch 2026 course and wanted to say thank you.
I learned a tremendous amount throughout the journey. The project-based approach helped me gain much more confidence with Laravel, and I ended up building and publishing my own finished version of the final project:
https://github.com/specbit/idea
I also wanted to share a bit of constructive feedback. At times the course was quite challenging to follow, especially when there were cuts between recordings, demos moved very quickly, or the file being edited wasn't clearly identified. I also ran into a few situations where code or commands didn't seem to work exactly as shown, particularly on a Windows machine using VS Code, which sometimes made troubleshooting difficult.
Coming from a C#/.NET background rather than PHP, the learning curve was already significant, so those moments could be especially challenging. Even so, working through those difficulties taught me a lot and gave me a much better understanding of Laravel and the PHP ecosystem.
That said, despite the frustrations along the way, I stuck with it because the content itself was valuable. Looking back, I'm genuinely grateful for the effort that went into creating the course and for everything I learned from it.
Thanks again for the teaching and for helping me get much deeper into Laravel.
Best regards,
Nuno Miguel Gomes (SpecBit)
SpecBit wrote a comment+100 XP
1w ago
SpecBit wrote a comment+100 XP
1w ago
SpecBit wrote a comment+100 XP
1w ago
@yoeriboven here's mine, help yourself https://github.com/specbit/idea
SpecBit wrote a comment+100 XP
1w ago
SpecBit wrote a comment+100 XP
1w ago
@LaramanCode Honestly, I share the same frustrations you mentioned. I’d even say that unless you’re already a mid‑level developer in PHP, Laravel, CSS, HTML, and JS, you definitely had a hard time following along. And even for native English speakers, I’m sure many people got lost along the way.
SpecBit liked a comment+100 XP
1w ago
We all know the quality of Jeffrey's courses, but this one certainly falls short of standards. It's unacceptable that Jeffrey's face takes up as much screen space as the code editor itself, especially considering the lack of essential line numbering (and no Soft-Wrap), the absence of a reference to the current file, and the lack of a Git repository. The screen changes so quickly after writing code that you don't even realize you've finished, and the same goes for imports. It seems this is a demonstration of how quickly the instructor uses the IDE, and that the student's learning isn't the priority.
Lessons like this are incredibly frustrating, especially for non-native English speakers.
SpecBit wrote a comment+100 XP
2w ago
After 6 hours to do 23 minutes of lesson 😅, here are the pieces that finally worked for me.
One important note: my route name is idea.show, not ideas.show.
it('edits an existing idea', function () {
$this->actingAs($user = User::factory()->create());
$idea = Idea::factory()->for($user)->create();
visit(route('idea.show', $idea))
->resize(1024, 768)
->click('@edit-idea-button')
->fill('title', 'Test Idea')
->click('@status-completed-button')
->fill('description', 'This is a test idea created during browser testing.')
->fill('@new-link', 'https://laracast.com')
->click('@submit-new-link-button')
->fill('@new-link', 'https://laravel.com')
->click('@submit-new-link-button')
->fill('@new-step', 'First step of the idea')
->click('@submit-new-step-button')
->fill('@new-step', 'Second step of the idea')
->click('@submit-new-step-button')
->click('Update')
->assertRoute('idea.show', [$idea]);
$idea = $user->ideas()->first();
expect($idea)->toMatchArray([
'title' => 'Test Idea',
'description' => 'This is a test idea created during browser testing.',
'status' => 'completed',
]);
expect($idea->links->toArray())->toBe([
$idea->links[0],
'https://laracast.com',
'https://laravel.com',
]);
$idea = $user->ideas()->with('steps')->first();
expect($idea->steps)->toHaveCount(2);
expect($idea->steps->pluck('description')->toArray())->toBe([
'First step of the idea',
'Second step of the idea',
]);
});
In my UpdateIdea action:
DB::transaction(function () use ($idea, $data, $attributes) {
$idea->update($data);
$idea->steps()->delete();
$idea->steps()->createMany($attributes['steps'] ?? []);
});
In my CreateIdea action:
DB::transaction(function () use ($data, $attributes) {
$idea = $this->user->ideas()->create($data);
$idea->steps()->createMany($attributes['steps'] ?? []);
});
And in the Blade/Alpine steps section:
<div>
<fieldset class="space-y-2">
<legend class="label">Actionable Steps</legend>
<template x-for="(step, index) in steps" :key="step.id || step.temp_id">
<div class="flex gap-x-2 items-center">
<input
:name="`steps[${index}][description]`"
x-model="step.description"
class="input"
>
<input
type="hidden"
:name="`steps[${index}][is_completed]`"
:value="step.is_completed ? '1' : '0'"
class="input"
>
<button
type="button"
class="cursor-pointer form-muted-icon"
aria-label="Remove step button"
@click="steps.splice(index, 1)"
>
<x-icons.close />
</button>
</div>
</template>
<div class="flex gap-x-2 items-center">
<input
x-model="newStep"
id="new-step"
data-test="new-step"
placeholder="What needs to be done?"
class="input flex-1"
spellcheck="false"
>
<button
type="button"
data-test="submit-new-step-button"
class="cursor-pointer form-muted-icon"
@click="
steps.push({
temp_id: Date.now(),
description: newStep.trim(),
is_completed: false
});
newStep = '';
"
:disabled="newStep.trim().length === 0"
aria-label="Add step button"
>
<x-icons.close class="rotate-45" />
</button>
</div>
</fieldset>
</div>
The key parts for me were:
:key="step.id || step.temp_id"
and using :value on the hidden input instead of x-model:
:value="step.is_completed ? '1' : '0'"
Also, I’m using is_completed instead of completed, so adjust that part if you followed Jeffrey’s naming exactly.
SpecBit wrote a comment+100 XP
2w ago
@jamiethomas1 Issue a ticket. Someone did that in the last episode and got a return from Jeffery itself.
SpecBit liked a comment+100 XP
2w 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.
SpecBit wrote a comment+100 XP
2w ago
@laravel2026 hi. Paste your AlpineJS @click() and <input> from the Actionable Steps block
SpecBit liked a comment+100 XP
2w ago
@liammills With the help of GPT, I found what seems to be a simpler solution. I replaced the x-model expression with value in the line
<input type="hidden" :name="`steps[${index}][completed]`" :value="step.completed ? '1' : '0'" class="input">
and everything started working like in Jeffrey’s example.
SpecBit liked a comment+100 XP
2w 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.
SpecBit wrote a comment+100 XP
2w ago
@codeplumber how did you opened a ticket? Episode 8 has one cut, if I'm not mistaken. So far this was the first time seeing @jeffreyway interact. Maybe I can't because mine's a free account. ty
SpecBit liked a comment+100 XP
2w ago
@mrrobotoh I have submitted a support ticket as it seems the issue is still here.
SpecBit wrote a comment+100 XP
2w ago
Why didn't he go right to the demo with the constructor? It's a simple principle in OOP, why all this narrative? Just so in the last 2min you'd have to refactor your CreateIdea class...
SpecBit liked a comment+100 XP
2w ago
@radmax @andrewdv8 @halvarado77
Solution for Browser Test Issues with File Upload Forms
I experienced the same issue and found a solution based on a comment from the next video. The problem occurs when using enctype="multipart/form-data" on forms with Playwright browser tests.
The Fix
The solution is to dynamically set the enctype attribute using Alpine.js, only when a file is actually selected.
Step 1: Update the form's x-data
In resources/views/idea/index.blade.php, add a new hasImage property to track when a file is selected:
<form
x-data="{
status : 'pending',
newLink: '',
links: [],
newStep: '',
steps: [],
hasImage: false
}"
action="{{ route('idea.store') }}"
method="POST"
class="space-y-4"
x-bind:enctype="hasImage ? 'multipart/form-data' : false"
>
Step 2: Replace static enctype with dynamic binding
Replace the static enctype="multipart/form-data" attribute with:
x-bind:enctype="hasImage ? 'multipart/form-data' : false"
Step 3: Update the file input
Add an @change event to the file input to toggle hasImage when a file is selected:
<input
type="file"
name="image"
accept="image/*"
@change="hasImage = $event.target.files.length > 0"
/>
Why This Works
This approach prevents the form from using multipart/form-data encoding when no image is uploaded, which resolves conflicts with Playwright browser tests. The enctype is only set when an actual file is selected by the user.
Benefits
- ✅ Browser tests pass successfully without modifications to Composer packages
- ✅ File uploads still work correctly for end users
- ✅ No impact on form functionality
- ✅ Clean, maintainable solution using Alpine.js
Test Results
After implementing these changes:
PASS Tests\Browser\CreateIdeaTest
✓ it creates a new idea (1.95s)
Tests: 1 passed (9 assertions)
Duration: 2.42s
Hope this helps!
SpecBit wrote a comment+100 XP
2w ago
@Alexandru019 thank you so much. In my case I had to add another step to maek it work. I posted about it and mentioned you.
SpecBit wrote a comment+100 XP
2w ago
I had to change my test code slightly. Mine was still failing after the AlpineJS fix because $idea->links was being cast to an ArrayObject, while the test was comparing it directly to a PHP array.
$idea = $user->ideas()->first();
expect($idea)->toMatchArray([
'title' => 'Test Idea',
'description' => 'This is a test idea created during browser testing.',
'status' => 'completed',
]);
expect($idea->links->toArray())->toBe([
'https://larcast.com',
'https://laravel.com',
]);
Converting the links collection with ->toArray() fixed it for me.
SpecBit wrote a comment+100 XP
3w ago
@liammills try/catch wrap is what I've been instructed to do during my C# .NET studies.
SpecBit liked a comment+100 XP
3w 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.');
}
}
SpecBit liked a comment+100 XP
3w ago
@kumarayush Did you use the PATCH method in the form? Remember, HTML forms only support GET and POST.
The solution is to use POST and add a hidden input called _method, like this:
<form method="POST" action="{{ route('step.update', $step) }}">
<input type="hidden" name="_method" value="PATCH">
</form>
But in Laravel, you can use the helper directive instead:
<form method="POST" action="{{ route('step.update', $step) }}">
@csrf
@method('PATCH')
<!-- your inputs here -->
<button type="submit">Update</button>
</form>
That way Laravel will correctly handle the PATCH request.
SpecBit wrote a comment+100 XP
3w ago
@iamGus like @tlclassless-34322681 said, I also have that extension installed and some for laravel. The thing is that sometimes they conflict with each other, and for some functions the hover just does nothing.
SpecBit wrote a comment+100 XP
3w ago
1:48 coderabbit made a mistake on the closing div, just above $slot
SpecBit wrote a comment+100 XP
4w ago
Is it just me, or was the forms.css code refactored off‑camera? I thoroughly checked his code to match mine a couple of episodes ago.
SpecBit wrote a comment+100 XP
4w ago
@lara_dev_1970 hi. I searched the github repo https://github.com/laracasts/Tweety/tree/master and couldn't find the code for this episode. Seems like they had a project and abandoned it... No support whatsoever, which translates in long hours of research to learn some topics but far worse it's to replicate the code on windows and vs code. Thank you so much
SpecBit liked a comment+100 XP
4w ago
SpecBit wrote a comment+100 XP
1mo ago
I wish I had the exact same tools that Jeffery has. At 10:28 if no record is shown in the db run dispatch(App\Jobs\UpdateIdeaStatistics); in tinker. Almost every episode something came up that I couldn't replicate whatching the videos, it's frustrating.
SpecBit wrote a comment+100 XP
1mo ago
Jeffery you should be aware that the last idea may not have been created by the first user_id. You should have had that in consideration and done something like $idea = App\Models\Idea::where('user_id', $john->id)->latest()->first();
SpecBit wrote a comment+100 XP
1mo ago
@bulat I was about to mention that. Lost so much time. No one from the team reads this!!! It's preposterous, so sad :( I wonder if for the paying subscribers it's the same deal?? anyway, ty
SpecBit wrote a comment+100 XP
1mo ago
@jake83 I totally agree with you. This videos are edited. 4 episodes ago there's a massive cut on the video, you can't really see what code he changes and i trully think it was an editor's mistake. Strange that Jeffery himself or his team, do not even reply to the comments.