DoeJohn

DoeJohn

Member Since 2 Years Ago

Experience Points 2,600
Experience
Level
Lessons Completed 3
Lessons
Completed
Best Reply Awards 0
Best Answer
Awards
  • Start Your Engines Achievement

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • First Thousand Achievement

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • One Year Member Achievement

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • Two Year Member Achievement

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • Three Year Member Achievement

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • Four Year Member Achievement

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • Five Year Member Achievement

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • School In Session Achievement

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • Welcome To The Community Achievement

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • Full Time Learner Achievement

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • Pay It Forward Achievement

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • Subscriber Achievement

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • Lifer Achievement

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • Laracasts Evangelist Achievement

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • Chatty Cathy Achievement

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • Laracasts Veteran Achievement

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • Ten Thousand Strong Achievement

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • Laracasts Master Achievement

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • Laracasts Tutor Achievement

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • Laracasts Sensei Achievement

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • Top 50 Achievement

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

24 Aug
2 months ago

DoeJohn left a reply on Why Having 'replies_count' In DB Table When There Is WithCount?

@Cronix If I'm already having 'replies_count' in DB table, then I gues that I should do the same for the "likes/dislikes" count, or articles's comments count... Interesting, I never thought about possible performance-issues when using withCount on very large tables (millions and millions of rows). I guess I'll avoid it from now on and store count as a single column in the DB table :)

23 Aug
2 months ago

DoeJohn started a new conversation Why Having 'replies_count' In DB Table When There Is WithCount [Lets Build A Forum In Laravel]

I was looking at @JeffreyWay 's code from the course Lets Build a Forum in Laravel and I saw that in the migration file for creating the threads table there is:

    public function up()
    {
        Schema::create('threads', function (Blueprint $table) {
            . . .

            $table->unsignedInteger('replies_count')->default(0);

            . . .
        });
    }   

... and then in Reply model he is having this:

    /**
     * Boot the reply instance.
     */
    protected static function boot()
    {
        parent::boot();
        static::created(function ($reply) {
            $reply->thread->increment('replies_count');
        });
        static::deleted(function ($reply) {
            $reply->thread->decrement('replies_count');
        });
    }

But Laravel has withCount- why didn't he use that? Is there any reason to avoid using withCount and store replies_count into the DB?

Maybe this has to do with performance? I guess because if there are millions of posts stored in the posts DB table - using withCount can be slow, right?

15 Jul
4 months ago

DoeJohn started a new conversation Any Risks Of Increasing Session Lifetime?

After installing Laravel 5, the session lifetime is set to 120 minutes:

    /*
    |--------------------------------------------------------------------------
    | Session Lifetime 
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to immediately expire on the browser closing, set that option.
    |
    */

    'lifetime' => env('SESSION_LIFETIME', 120),

I would like to increase it to, let's say, 360 minutes or more but I wonder if there is some reason why I should avoid long session lifetime... Is there any risk or disadvantage?

11 Jul
4 months ago

DoeJohn started a new conversation Laravel File Storage: How To Store (decoded) Base64 Image?

How to store base64 image using the Laravel's filesytem (File Storage) methods?

For example, I can decode base64 image like this:

base64_decode($encoded_image);

but all of the Laravel's methods for storing files can accept either a Illuminate\Http\File or Illuminate\Http\UploadedFile instance.

So I guess I'd have to convert base64 image (or decoded base64 image) to Illuminate\Http\File or Illuminate\Http\UploadedFile, but how?

DoeJohn started a new conversation Store Resized Decoded Base64 Image Using Laravel's Filesystem (Intervention Image Encode() Doesn't Work)

  • I am using Croppie jQuery plugin which returns the cropped image encoded in base64.

  • After submitting the form (with the cropped image encoded in base64) - I decode & resize it using the Intervention Image library:

        public function decodeResizeAndStore(Request $request)
        {
            $croppie_code = $request->croppie_code;
            
            // https://stackoverflow.com/a/11511605/4437206
            if (preg_match('/^data:image\/(\w+);base64,/', $croppie_code, $type)) {
                $encoded_base64_image = substr($croppie_code, strpos($croppie_code, ',') + 1);
                $type = strtolower($type[1]);
    
                $decoded_image = base64_decode($encoded_base64_image);
    
                $resized_image = Image::make($decoded_image)->resize(300, 200);
    
                // AND NOW I WANT TO STORE $resized_image using Laravel filesystem BUT...
            }
        }

Finally, I want to store the resized image using Laravel's filesytem (File Storage) and that's where I'm stuck - when I try this:

    Storage::put($path, (string) $resized_image->encode());

... it doesn't work. Actually, it is working something - it looks like there is some memory leak or something, the browser's tab freezes, my RAM & CPU usage go high...

So I just tried:

    dd($resized_image->encode());

... and yes, this is where it definitely crashes - when using encode() method.

I am not sure why, maybe this is happening because I'm not working with a standard image upload but with decoded base64?

But, on the other side, Intervention Image can create a new image instance from base64: http://image.intervention.io/api/make ... and, in my case, this works OK:

$resized_image = Image::make($decoded_image)->resize(300, 200);

I could then use the save() method and everything would work OK. But I need to use Laravel's File Storage.

Do you know how I can handle this?

27 Jun
4 months ago

DoeJohn started a new conversation Laravel Route Parameters Not Trimmed (it Normally Works With When Whitespace Is Added)

I have the following route in web.php:

    Route::get('posts/{encoded_id}/{slug}', '[email protected]')

... and it works fine:


    http://example.test/posts/1Dl89aRjpk/this-is-some-title

But the "problem" is that it will also work when I add a white space at the end of route parameter {encoded_id}:

    http://example.test/posts/1Dl89aRjpk /this-is-some-title

    // or

    http://example.test/posts/1Dl89aRjpk%20 /this-is-some-title

    // or

    http://example.test/posts/1Dl89aRjpk%20%20 /this-is-some-title

With whitespace added at the end - this will work normally and there is no 404:

    Post::where('encoded_id', $encoded_id)->firstOrFail();

... but why?

Maybe because of the type of field in the DB (CHAR)?

    $table->char('encoded_id', 10)

Or maybe it has something to do with .htaccess (I'm using XAMPP / Windows)?

I'm using Laravel 5.6.

26 Jun
4 months ago

DoeJohn started a new conversation Laravel Validation Of <input Type=“text”>: Why 'required|string'?

Correct me if I'm wrong:

Correct me if I'm wrong:

If we have some form with <input type="text" name='username'> - when we enter something in that field (when it's not empty) and submit the form, that value will always be string on server-side (Laravel), right? $request->username will be a string?

I am asking this because I saw that in Laravel's authentication system there are validation rules like this:

    public function login(Request $request)
    {
        $validator = Validator::make($request->all(), [
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    . . .

but I don't understand what's the point of having both required and string as validation rules? Isn't it enough to have only required because anything entered in that field will always be string?

Even when the input field is date or numeric - the submitted value will always be a string ($request->numeric_field will be string).

22 Jun
4 months ago

DoeJohn left a reply on Markdown VS HTML + Purifier? Please Help Me Decide!

I used BBCode before on some of my projects and I really like it:

PROS:

  • Easy to understand & more user-friendly than Markdown or HTML;
  • There are some good WYSIWYG BBCode editors such as SCEditor (
  • Finally, it's easy to add custom tags (rules), for example:
[youtube]https://www.youtube.com/watch?v=2pLL00WR5iU[/youtube]

or

[youtube]2pLL00WR5iU[/youtube]

CONS:

I've always had trouble finding good, fast and actively maintained parser/converter.

There are not many packages for "parsing"/converting BBCode to HTML, and most of them are based on kaimallea's regex "parser" which fails at complex, nested tags. In fact, every Laravel BBCode converter package uses kaimallea's regex solution.

There are a few other PHP solutions (packages) that are available, but none of them is actively maintained and each has some fails* at complex, especially when it comes to nested tags.

phpBB and MyBB have really good BBCode converters, which are also based on regex but way more complex than kaimallea's solution.

I wonder if it's somehow possible to get their converters and use them in our (Laravel) PHP projects? I remember that once I tried to analyze how phpBB's converter works (in order to use it in my Laravel project), but the code was too complex for me and I didn't have enough time.

It would be really great if we could use phpBB or MyBB converters in our own PHP (Laravel) projects!

DoeJohn left a reply on Markdown VS HTML + Purifier? Please Help Me Decide!

@Vilfago @Nash Thanks for the answers.

You should use Purifier in any case, since your Markdown will be converted into HTML when displayed on your site.

Hmm, I was thinking that you don't need Purifier when using CommonMark because if html_input is set to escape or stripand allow_unsafe_links is set to false- according to the documentation - you should be safe, right?

21 Jun
4 months ago

DoeJohn started a new conversation Markdown VS HTML + Purifier? Please Help Me Decide!

I'm creating a website where users can submit:

  • Blog posts (Articles)
  • Comments
  • Forum posts

Rich text is necessary and this website will be used by used by non-technical folks (usually older people) - therefore, WYSIWYG editor is required.

I have to decide between using Markdown or HTML. Here is how I currently see things in terms of good/bad:

#MARKDOWN

1. WYSIWYG EDITOR - I was quite surprised when I discovered that there are no Markdown WYSIWYG editors. Actually, the only one that is actively maintained: https://github.com/nhnent/tui.editor

I really liked that editor, it works great, but the only thing that I don't like is the size of it: minified version (tui-editor-Editor.min.js) is ~300 KB (maybe I'm wrong and that's not that big at all?)

Most of the other Markdown editor are not WYSIWYG. There are some editors that are something between. For example, SimpleMDE is not WYSIWYG (but "WYSIWYG-esque") and I want to avoid it (also, it looks like it is not actively maintained anymore).

If you know a good WYSIWYG Markdown editor that is actively maintained, please let me know.

2. YOUTUBE - YouTube videos can't be added directly (when using markdown). I know about some workarounds like this, but that's not an option.

This should not be a problem if one of the packages:

supports autolinking and provides the ability to add a custom rule which will recognize YouTube links and automatically embed them.

But I did not use these packages... so is that possible?

20 Jun
4 months ago

DoeJohn left a reply on Migrations: When To Use Index()?

@Cronix

I generally index columns that are 1) id's (including foreign keys)

Hm, as for the foreign keys - aren't they already indexes (as stated above)? Also, does this mean that you also index primary key (id)?

Can you give me an example based on the migration code in the first post? Would you do it like this:

$table->primary('id');

$table->index('user_id');
$table->index('category_id');

or maybe you would add composite keys like this:

$table->index(['id', 'user_id']);
$table->index(['id', 'category_id']);

or maybe like this:

$table->index(['id', 'user_id', 'category_id']);

?

DoeJohn left a reply on Migrations: When To Use Index()?

@Swaz thanks for the link, I just entered my email address there and I'm waiting to get confirmation email in order to be able to watch those videos (their site is a bit confusing, or I'm blind - I cannot see standard login/register links)

DoeJohn left a reply on Migrations: When To Use Index()?

@click

If you sort a table based on the created_at date you probably want an index on that field

Yeah, I do that almost everywhere (I'm using Laravel's latest and oldest methods which, by default, order by created_at column).

DoeJohn left a reply on Migrations: When To Use Index()?

@martinbean

Your website will slow down and you’ll users reporting errors like “connection timeout” or “too many connections”.

But shouldn't we prevent (in advance) something like that, if possible?

DoeJohn left a reply on Migrations: When To Use Index()?

@martinbean

Foreign keys are a type of index. Does that mean that it's wrong to always use index() on foreign keys (as @Sergiu17 wrote "Basically, if the column ends in _id it should be indexed."?

You should only really be looking at indexes if you have heavy database load and looking to speed up queries.

How will I know if there will be have heavy database load?

As for the WordPress - there are indexes on some fields such as post_author and post_parent (which are the only foreign keys in _wp_posts table) as well as post_date which is equivalent to Laravel's created_at...

And I'm referring to WordPress as an example because it will alwas have these indexes regardless of whether there is heavy database load.

DoeJohn left a reply on Migrations: When To Use Index()?

I'm just looking how WordPress indexed columns in the :_wp_posts table:

https://codex.wordpress.org/Database_Description#Table:_wp_posts

Indexes:

|Keyname|Type|Field| |--- |--- |--- | |PRIMARY|PRIMARY|ID| |post_name|INDEX|post_name| |type_status_date|INDEX|post_typepost_statuspost_dateID| |post_parent|INDEX|post_parent| |post_author|INDEX|post_author|

DoeJohn left a reply on Migrations: When To Use Index()?

Also, what about primary key (id)? Shoudl I have:

$table->index('id');

?

DoeJohn left a reply on Migrations: When To Use Index()?

What about timestamps (created_at & updated_at)? Like this:

$table->index('created_at');
$table->index('updated_at');

?

DoeJohn started a new conversation Migrations: When To Use Index()?

Should I addindex() for every field? Or just use it on foreign keys? Or?

For example, here's one migration:


    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');

            $table->unsignedInteger('user_id');
            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');

            $table->unsignedInteger('category_id');
            $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
         
            $table->string('title');
            $table->string('slug')->nullable();
            $table->string('image')->nullable();
            $table->text('body');
            $table->text('excerpt')->nullable();

            $table->boolean('approved')->default(0);

            $table->timestamps();

            $table->unique(['category_id', 'slug']);
        });
    }

Which fields in this migration should be indexed? For example, I always add them on foreign keys:

$table->index('user_id');
$table->index('category_id');

Is this wrong (to always use index() on foreign keys)?

Would you index some other fields in this migration? If yes, which one?

18 Jun
4 months ago

DoeJohn left a reply on @canany Or @canevery ? [Laravel 5.6]

Cool! I did composer update and now it works.

One question: I'm using PhpStorm 2018.1.3 (+ Laravel plugin + IDE Helper) and it does not recognize @canany in Blade files. I know that it is a new feature, but is there any way to make my PhpStorm recognize and suggest it?

17 Jun
5 months ago

DoeJohn started a new conversation @canany Or @canevery ? [Laravel 5.6]

There is @can Blade directive, but it accepts only one string (one ability). But what about checking multiple permissions at once? Is there something like:

@canany(['create_posts', 'edit_posts', 'delete_posts']) - checks if any one of specified abilities passes

or

@canevery(['create_posts', 'edit_posts', 'delete_posts']) - checks if every ability passes

?

I'm using Laravel 5.6

DoeJohn started a new conversation Laravel Policies: $this->authorizeResource('model'); Is Required In The Controller's Constructor?

I have UserPolicy:

<?php

namespace App\Policies;

use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class UserPolicy
{
    use HandlesAuthorization;

    public function create(User $user)
    {
        return $user->can('create_users');
    }

    public function update(User $user, User $model)
    {
        return $user->can('edit_users');
    }

    public function delete(User $user, User $model)
    {
        return $user->can('delete_users');
    }
}

I have UserPolicy:

09 Jun
5 months ago

DoeJohn left a reply on Display All Dates On Models In The User’s Timezone

@Cronix I ended up using your solutin because I needed a helper to format... and since I'm already using it - there's no need to have that trait.

Thanks :)

As for the serverDate helper - I guess that your function accepts a string (submitted by the user), then it creates a new Carbon instance from it:

$date = Carbon::parse($value, Auth::user()->timezone);

... and then returns string:

return $date->timezone(config('app.timezone')->format('Y-m-d H:i:s.u');

right? I'm asking because I'm not sure if it's better to do all that in the helper function or to just accept a Carbon instance which is already in user's timezone and then return $date->timezone(config('app.timezone')->format('Y-m-d H:i:s.u');.

08 Jun
5 months ago

DoeJohn left a reply on Display All Dates On Models In The User’s Timezone

@Cronix Yeah, I agree.

I'm just thinking about what potential problems might be with this overriding... and, for now, I don't see any. Hmmm...

asDateTime($value) in Concerns\HasAttributes trait always returns \Illuminate\Support\Carbon (it converts to an instance of Carbon).

And in that override - I just added (chained) ->timezone($timezone) before returning an instance of Carbon.

Everything else is the same, the method is the same as original and it returns the same thing - just "timezoned" (:D sorry, my English is not good).

DoeJohn left a reply on Display All Dates On Models In The User’s Timezone

@Cronix If the way described in my previous post (using trait that overrides asDateTime method) is OK, then I guess that we will only need a serverDatethat converts from user timezone to UTC for storing (if the user inputs a date...).

DoeJohn left a reply on Display All Dates On Models In The User’s Timezone

One way (without accessors) is to use this trait:

<?php

namespace App\Traits;

use DateTimeInterface;
use Illuminate\Support\Carbon;

trait UserTimezoneAware
{
    /**
     * Return a timestamp as DateTime object.
     *
     * @param  mixed  $value
     * @return \Illuminate\Support\Carbon
     */
    protected function asDateTime($value)
    {
        $timezone = auth()->check() ? auth()->user()->timezone : config('app.timezone');

        if ($value instanceof Carbon) {
            return $value->timezone($timezone);
        }

        if ($value instanceof DateTimeInterface) {
            return new Carbon(
                $value->format('Y-m-d H:i:s.u'), $timezone
            );
        }

        if (is_numeric($value)) {
            return Carbon::createFromTimestamp($value)->timezone($timezone);
        }

        if ($this->isStandardDateFormat($value)) {
            return Carbon::createFromFormat('Y-m-d', $value)->startOfDay()->timezone($timezone);
        }

        return Carbon::createFromFormat(
            str_replace('.v', '.u', $this->getDateFormat()), $value
        )->timezone($timezone);
    }
}

When using this trait, we are overriding asDateTime($value) defined in Concerns\HasAttributes trait (used in Illuminate\Database\Eloquent\Model).

This seems to work OK, I have not yet encountered any problems.

Do you see any risks or potential problems when doing this (when using this trait that overrides asDateTime method)?

DoeJohn started a new conversation Display All $dates On Models In The User’s Timezone

I have the users timezone stored (there is timezone column in the users DB table) and I want to display all $dates on all models in the in the user’s timezone, if authenticated.

I'm trying to find an elegant way to do this... Ideally, when there is something like this in my Blade views:

{{ $post->created_at }}

OR

{{ $post->created_at->format('h:i:s A') }}

... for authenticated users it would be automatically in their timezones.

How would you handle this?

I'm thinking about creating one trait (for example, app/Traits/UserTimezoneAware.php) and place there Accessors that will simply return Carbon::createFromFormat('Y-m-d H:i:s', $value)->timezone(auth()->user()->timezone) if the current user is authenticated. For example:

<?php

namespace App\Traits;

use Carbon\Carbon;

trait UserTimezoneAware
{
    /**
     * Get the created_at in the user's timezone.
     *
     * @param $value
     * @return mixed
     */
    public function getCreatedAtAttribute($value)
    {
        if (auth()->check()) {
            return Carbon::createFromFormat('Y-m-d H:i:s', $value)->timezone(auth()->user()->timezone);
        }

        return Carbon::createFromFormat('Y-m-d H:i:s', $value);
    }

    /**
     * Get the updated_at in the user's timezone.
     *
     * @param $value
     * @return mixed
     */
    public function getUpdatedAtAttribute($value) { ... }
}

But I'm not sure if this this is good or bad to do (to create these accessros for the Laravel's $datesattributes)?

Also, models will have different attributes specified in $dates array: for example, User model may have:

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = [
        'created_at',
        'updated_at',
        'last_login_at'
    ];

and Post model can have:

    protected $dates = [
        'created_at',
        'updated_at',
        'approved_at',
        'deleted_at'
    ];

Is it possible to dynamically create accessors in trait, based on the attirbutes specified in $dates array of a model that uses that trait?

Or maybe there is a better way to handle this, without accessors?

07 Jun
5 months ago

DoeJohn left a reply on Extracting Code (logic) Into Service-class: Additional Checks & Returning Error Messages To The User

@bobbybouwmann I didn't know about this: https://stackoverflow.com/a/47223679/4437206

Just throwing that is enough - I don't need to cach anything in controller. For now, I think that this is the best (or the least bad solution) :)

06 Jun
5 months ago

DoeJohn started a new conversation What Is Your "upload Strategy"? (directory Structure & Naming Uploaded Images)

When a user uploads an image to your site - what is your upload / storage strategy?

For example, lets say that users can upload:

  • Avatars (profile images)
  • Images in their forum posts
  • Images in their articles

1. How would you organize the directory structure?

Would you just have three sub-directories in public/images (or maybe in public/uploads?):

  • One for avatars: public/images/avatars (or public/uploads/avatars or public/images/uploads/avatars?)
  • One for forum posts: public/images/forum
  • One for articles: public/images/articles

?

Or you would just have one public/uploads directory and put everything there, no matter if it's an avatar, or uploaded image for forum post/article?

Or, for example, you would create username subfolders in the uploads directory... ?

2. Would you put all of the uploaded files (images) inside one directory? (for example, all uploaded avatars would be inside one directory)

If yes, then you could end up with thousands (or even hundreds of thousands) images (avatars) inside one directory and, as I understand it, this could be bad for many reasons.

If no, then what approach would you use? I know these two ways:

3. Finally, how would you name uploaded images (avatas, forum post images, article images...)?

DoeJohn left a reply on Extracting Code (logic) Into Service-class: Additional Checks & Returning Error Messages To The User

@martinbean Hi,

In my previous reply to @bobbybouwmann I tried to explain why I think that it's not possible to move all those checks into Form Request class (especially in case of saveAvatar method which validates base64 image, type and checks whether the decoding was successful...

As I mentioned in the opening post, there is:

  • One page (one form) for editing profile: here authenticated users (auth()->user()) can edit their own profile. On that page there is one form where they can set their avatar, name, timezone and some other stuff;
  • One separate page (form) for editing email: here authenticated users (auth()->user()) can change their email;
  • One separate page (form) for editing password...

On the other side, admin users have access to the /admin area where they can edit other users. When they edit user (/admin/users/{id}/edit) - there is only one big form which includes everything: profile (avatar, name, timezone...) + email + passowrd.

So, as for the taking a resource approach, I'm still not sure how to apply it in this case in order to avoid repeating the same logic (code) in the controllers used by the regular users and controllers used by the admin users (controllers that are in app/Http/Controllers/Admin subdirectory)...

I usually did that with using service-classes, but again - I have some specific cases here (as I described in my post above).

Sorry, my English is not good :)

DoeJohn left a reply on Extracting Code (logic) Into Service-class: Additional Checks & Returning Error Messages To The User

@bobbybouwmann Hello bobby :)

I am already using Form Requests Validation, but the problem is:

  • Every user can use "static" or "dynamic" avatar, and this can be set on the "profile settings" page.
    • If a user is using dynamic avatar - it can be retrieved from Gravatar or social media (when they login with Facebook or Twitter account)...
    • If a user chooses to use "static avatar" (on his "profile settings" page) - then he must select some image, crop it and then he can save the changes (update his profile). But I'm using Croppie which encodes the cropped image with base64, and that base64 code is stored in the hidden input. This means that there is no imaage being uploaded, there is only base64 in the hidden input.

So I have to do some checks concerning the user's choice of a static or dynamic avatar, based on whether he had previously used a static or dynamic avatar ... and I don't think it's possible to move all those checks into Form Request class. For example, here is my setAvatar method in UserService:

class UserService
{
    /**
     * Set the avatar for the specified user.
     *
     * @param Request $request
     * @param User $user
     * @return User|\Illuminate\Http\RedirectResponse
     */
    public function setAvatar(Request $request, User $user)
    {
        // If static (custom) avatar was selected:
        if (! $request->dynamic_avatar) {
            // If the user has changed from dynamic to static (custom) avatar, he or she has to select an image and
            // crop it using Cropper.js, which will store encoded (base64) avatar in 'avatar_code' hidden input.
            // Therefore, we treat 'avatar_code' as required if the user has changed from dynamic to static (custom) avatar:
            if ($user->dynamic_avatar && ! $request->avatar_code) {
                return back()->withInput()->withErrors(['avatar_not_selected' => 'Please upload an avatar']);
            }

            if ($request->avatar_code) {
        // SOME CODE
                
                // Save new avatar in the filesystem and set new the path:
                $user->avatar = $this->saveAvatar($request, $user);
            }
            
        // SOME CODE
        }

        // SOME CODE

        $user->dynamic_avatar = $request->dynamic_avatar;

        return $user;
    }
    
    . . .

Even more tricky (for me) is the protected method saveAvatar($request, $user) (also in UserService), because it validates base64 image, then its type (checks the if the type is. jpg or .png), decodes it (checks if the decoding failed) - and as far as I know - it is not possible to do all that in Form Request class:

    protected function saveAvatar(Request $request, User $user)
    {
        $avatar_code = $request->avatar_code;

        if (preg_match('/^data:image\/(\w+);base64,/', $avatar_code, $type)) {
            $avatar_base64 = substr($avatar_code, strpos($avatar_code, ',') + 1);
            $type = strtolower($type[1]);

            if (! in_array($type, ['jpeg', 'png', 'jpg'])) {
                // invalid type
                return back()->withInput()->with('error', 'some message about invalid type...');
            }

            $avatar_image = base64_decode($avatar_base64);

            if ($avatar_image === false) {
                // base64_decode failed
                return back()->withInput()->with('error', 'some message...');
            }
        } else {
            // did not match data URI with image data
            return back()->withInput()->with('error', 'some message...');
        }

    // some code
        
        return $avatar_path;
    }
    
    . . .

Maybe throwing exceptions (as you mentioned as second option) is the only way to do it in this case, but I'm not sure :)

05 Jun
5 months ago

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait Or SERVICE CLASS

@bobbybouwmann Hello bobby, I am using service-class to extract logic, but I am having some problems with returning some sepific error messages back to the user. Doing something like:

return back()->withInput()->with(['error' => 'Some specific error message about $something']);

from service-class doesn't work, but I don't know how to hande... I started new discussion about that here:

https://laracasts.com/discuss/channels/code-review/extracting-code-logic-into-service-class-additional-checks-returning-error-messages-to-the-user

There I described in detail what bothers me :) Basically, one of the methods in my UserService has a lot of checks (if statements) and in many cases I need to return back to the user with the specific error message.

I know you're a very busy man, but if by any chance you have surplus time, you're bored and you don't know what to do - you can take a look at that topic :D :) :smile:

DoeJohn started a new conversation Extracting Code (logic) Into Service-class: Additional Checks & Returning Error Messages To The User

I am creating some simple web application with the user system:

  • Regular users can edit their profile (avatar, name, timezone), change password and change their email. There are 3 separate pages for that, one for editing profile (e.g. www.example.com/edit_profile), one for changing password (e.g. www.example.com/edit_passowrd) and one for changing email (e.g. www.example.com/edit_email).
  • Admins can go to the /admin area and edit profile (avatar, name, timezone), password and email of other user accounts. There is only one page for doing that, for example www.example.com/admin/users/{id}/edit.

There are two controllers, one is UserController for regular users, and the other one is in the Admin subdirectory (Admin/UserController) and it is used when admins edit users.

Because of this I am having a situation where UserController and Admin/UserController actions use the same code/logic, so I decided to extract that logic into its own service-class in app/Services/UserService.

For editing email I have one method in the UserService which I then use in both UserController & Admin/UserController. Then I have one method for editing the name, then one for editing password and so on... and I'm using them like this:

   public function __construct(UserService $userService)
   {
       $this->userService= $userService;
   }
   
   // In `Admin/UserController`:
   public function update(Request $request, $id)
   {
       // Some code
       
       $user = $this->userService->setAvatar(Request $request, $user);
       $user = $this->userService->setName($request->name, $user);
       $user = $this->userService->setTmezone($request->timezone, $user);
       $user = $this->userService->setEmail($request->email, $user);
       $user = $this->userService->setPassword($request->password, $user);
       . . .
       $user->save();
   
       return back()->with('success', 'Updated!');
   }

   // In `UserController`:
   public function updateProfile(Request $request)
   {
       // Some code
       
       $user = $this->userService->setAvatar(Request $request, auth()->user());
       $user = $this->userService->setName($request->name, auth()->user());
       $user = $this->userService->setTmezone($request->timezone, auth()->user());

       . . .
       $user->save();
   
       return back()->with('success', 'Updated!');
   }

The problem is with the setAvatar(Request $request) method - it has a lot of checks (if statements) and in many cases I need to return some specific error message to the user. For example:

    <?php
    
    namespace App\Services;
    
    use App\User;
    use DB;
    use Illuminate\Http\Request;
    use Intervention\Image\ImageManagerStatic as Image;
    
    class UserService
    {
        /**
         * Set the avatar for the specified user.
         *
         * @param Request $request
         * @param User $user
         * @return User|\Illuminate\Http\RedirectResponse
         */
        public function setAvatar(Request $request, User $user)
        {
            // SOME CODE . . .
    
            if ($something) {
                 return back()->withInput()->withErrors(['avatar' => 'Please upload an avatar']); // <=========
             }
    
            // SOME CODE . . .

            if ($something_else) {
                 return back()->withInput()->with(['error' => 'some specific error message']); // <=========
            }

            // SOME CODE . . .

            if ($foo === $bar) {
                 if (! $request->avatar && $something) 
                 return back()->withInput()->with(['error' => 'another specific error message']); // <=========
            }

            // SOME CODE . . .
    
            $user->avatar = $this->saveAvatar($request, $user);
    
            return $user;
        }
    }

As you can see - in many cases I need to return \Illuminate\Http\RedirectResponse with the specific error message:

            if ($something) {
                 return back()->withInput()->with(['error' => 'Some specific error message about $something']);
            }

Of course, all these redirects will not work when I use this method in the controller:

    public function updateProfile(Request $request)
    {
        // Some code
    
        $user = $this->userService->setAvatar(Request $request, auth()->user());
    
        // Some code
    
        $user->save();
    
        return back()->with('success', 'Updated!');
    }

... and I understand why (it's clear), but I don't know how to handle this. I am not sure what is the proper way do it.

I was thinking about returning false instead of returning back()->withInput()->with(['error' => 'Some specific error message about $something']); - but then I will not know which check (if statement) returned false and what error message to show to the user.

Other way would be to return something like:

    ['status' => 'error', 'message' => 'specific message'] 
    
    or
    
    ['status' => 'ok', 'user' => $user] 

... and then in my controller I would do something like this:

    $resonse = $this->userService->setAvatar($request, auth()->user());
    if ($resonse['status'] === 'error') return back()->withInput()->with('error', $resonse['message']);
    if ($resonse['status'] === 'ok') $user = $resonse['user'];

... but I'm not sure if that's the right way to do it. Or maybe my approach with the class-service is completely wrong?

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait

@bobbybouwmann Hello, it's me again :)

What if we need to do some additional checks in the class or service - how can we handle if these checks fail and we need to redirect back with the specific error message?

For example, let's say that we have UserService with the methods setAvatar and saveAvatar:

<?php

namespace App\Services;

use App\User;
use DB;
use Illuminate\Http\Request;
use Intervention\Image\ImageManagerStatic as Image;

class UserService
{
    /**
     * Set the avatar for the specified user.
     *
     * @param Request $request
     * @param User $user
     * @return User|\Illuminate\Http\RedirectResponse
     */
    public function setAvatar(Request $request, User $user)
    {
        . . .

        if (! $request->foo) {
            if ($user->foo && ! $request->bar) {
                return back()->withInput()->withErrors(['avatar_not_selected' => 'Please upload an avatar']); // <===================== THIS!
            }

            if ($request->bar) {
                // Save new avatar in the filesystem and set new the path:
                $user->avatar = $this->saveAvatar($request, $user);
            }
        }

        . . .

        $user->foo = $request->foo;

        return $user;
    }

    /**
     * Decode and save the base64 avatar image in the filesystem.
     *
     * @param Request $request
     * @param User $user
     * @return \Illuminate\Http\RedirectResponse|string
     */
    protected function saveAvatar(Request $request, User $user)
    {
          . . .

        // https://stackoverflow.com/a/11511605/4437206
        if (preg_match('/^data:image\/(\w+);base64,/', $avatar_code, $type)) {
            $avatar_base64 = substr($avatar_code, strpos($avatar_code, ',') + 1);
            $type = strtolower($type[1]);

            if (! in_array($type, ['jpeg', 'png', 'jpg'])) {
                // invalid type
                return back()->withInput()->with('error', $error_message); // <===================== THIS!
            }

            $avatar_image = base64_decode($avatar_base64);

            if ($avatar_image === false) {
                // base64_decode failed
                return back()->withInput()->with('error', $error_message); // <===================== THIS!
            }
        } else {
            // did not match data URI with image data
            return back()->withInput()->with('error', $error_message); // <===================== THIS!
        }

    . . .

    return $avatar_path;
    }
}

As you can see - there are some checks in these methods and if they fail - we return RedirectResponse... for example:

if (something is wrong) {
    return back()->withInput()->with('error', 'some specific message');
}

But when I use this in [email protected] like this:

    public function updateProfile(UserProfileFormRequest $request)
    {
  
        $user = $this->userService->setAvatar($request, auth()->user());

        $user->save();

        return back()->with('success', 'Your profile has been successfully updated.');
    }

This will not work (it's clear why - if some of methods return RedirectResponse (back()->withInput()->with('error', 'some specific message');) instead of User $user...).

But how should I organize this, I wanted to extract that logic into its own service-class, but the problem are those checks in the methods of the service-class - returning RedirectResponse from there will not work.

I could return just false and then in the controller check if it returned false - but I will not know which check returned false and what error message to show to the user.

Other way would be to return something like:

['status' => 'error', 'message' => 'specific message'] 

or

['status' => 'ok', 'user' => $user] 

... and then in my controller I would do something like this:

$resonse = $this->userService->setAvatar($request, auth()->user());
if ($resonse['status'] === 'error') return back()->withInput()->with('error', $resonse['message']);
if ($resonse['status'] === 'ok') $user = $resonse['user'] ;

... but I'm not sure if this is the right way to do it.

09 May
6 months ago

DoeJohn started a new conversation Laravel Socialite - Scopes, Additional Information

In Laravel documentation there is a section about Access Scopes: https://laravel.com/docs/5.6/socialite#access-scopes

It is still unclear to me - what are Access Scopes, what are these scopes used for?

Correct me if I'm wrong, but I suppose they are used for getting additional information (for example, first and last name etc.), right?

If so, then I also assume that each provider (Facebook, Twitter, Google... ) has its own scopes (scope names), right?

If that's true, where can I find list of available scopes for:

  • Twitter
  • Facebook
  • Google

For example, I was googling "oauth twitter scopes" but didn't find anything. I also searched their documentations but I could not find anything about scopes...

28 Dec
10 months ago

DoeJohn started a new conversation Middleware - Add Data To $request (which Will Be Used In Controller Action)?

How to add data to the $request in Middleware?

I am asking this because currently in my PostsControllerI have:

    protected $group;
    protected $forum;
    protected $thread;

    public function __construct(PostService $postService)
    {
        /*
         * Find group, forum and thread by their slugs.
         * For example, if we have "http://example.com/forums/laravel/questions/some-title"
         * "laravel" will represent group slug, "questions" will represent forum slug and "some-title" will
         * represent thread.
         */
        $this->group = Forum::findApprovedGroupBySlug(request()->route('group_slug'));
        $this->forum = Forum::findApprovedForumBySlugAndGroup(request()->route('forum_slug'), $this->group);
        $this->thread = $this->forum->threads()->slug(request()->route('thread_slug'))->approved()->firstOrFail();
    }

... and I think that it would be good to move this to Middleware:

    public function handle($request, Closure $next)
    {
        $group = Forum::findApprovedGroupBySlug($request->route('group_slug'));
        $forum = Forum::findApprovedForumBySlugAndGroup($request->route('forum_slug'), $group);
        $thread = $forum->threads()->slug($request->route('thread_slug'))->approved()->firstOrFail();

        $request->request->add(['group' => $group, 'forum' => $forum, 'thread ' => $thread]);

        return $next($request);
    }

As you can see - I've tried to add data to $request in the following way:

$request->request->add(['group' => $group, 'forum' => $forum, 'thread ' => $thread]);

But it didn't work. Also, I've tried this:

$request->attributes->add(['group' => $group, 'forum' => $forum, 'thread ' => $thread]);

but it's the same - I don't know how to access these. For example, in my controller action I've tried:

dd(request()->request); 

and I see that they are there... but request()->request->thread is null. Also, request()->attributes, request()->thread etc... are null).

Also, is it good to add data to $request in Middleware? I didn't find anything about that in the Laravel Documentation, so maybe I shouldn't do that?

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait

@bobbybouwmann Haha, sorry, here's another very short question :D

Is there any reason not to make static methods in PostService? I'm asking becasue we don't have to do the follwoing:

protected $postService;

public function __construct(PostService $postService)
{
    $this->postService = $postService;
}

In controller action methods we would have something like PostService::store(...) or PostService::update(...)or PostService::destroy(...).

I would make it static, but who knows - maybe there's something I don't know about that, it's hiding and waiting to spook us when we least expect it :D

27 Dec
10 months ago

DoeJohn left a reply on Order By The Latest (most Recent) Related Model

@Snapey Hmm, but one problem would be when you edit (update) thread (parent model) - For example, when you change the title of thread -updated_at will be changed (touched). Also, what about deleting posts - when you delete the last post of a thread - will updated_at change to the previous value?

DoeJohn left a reply on Order By The Latest (most Recent) Related Model

Ok, there are 2 working solutions for this, I'm not sure which one is better but they both work:

1. Solution (using join and groupBy):

        $threads = $this->forum->threads()
            ->approved()
            ->join('posts', 'posts.thread_id', '=', 'threads.id')
            ->selectRaw('threads.*, MAX(posts.created_at) AS latest_post_at')
            ->groupBy('threads.id')
            ->orderByDesc('pinned')
            ->orderByDesc('latest_post_at')
            ->withCount(['posts AS approved_replies' => function ($query) {
                $query->where('posts.approved', true)->where('posts.is_starting_thread', false);
            }])
            ->with(['posts' => function ($query) { // Posts
                    $query->approved()
                        ->with('user') // Author of post
                        ->latest();
                }]
            )
            ->with('user')
            ->paginate(20);

If I understood correctly, in the Laravel documentation it is stated that there may be some problems when having both groupBy() and paginate() at the same time. But in this case it seems that everything is working OK. I'm not sure why, but I guess that's because we are grouping by thread id (->groupBy('threads.id')), not by some other column and that's why Larave's pagination works ??? (please correct me if I'm wrong)

2. Solution (without join and groupBy):

$this->forum->threads()
    ->approved()
    ->join('posts', 'posts.thread_id', '=', 'threads.id')
    ->select('threads.*', 'posts.created_at AS latest_post_at')
    ->whereNotExists(function ($subquery) {
        return $subquery->from('posts AS later_posts')
            ->whereRaw('later_posts.thread_id = posts.thread_id')
            ->whereRaw('later_posts.created_at > posts.created_at');
    })
    ->orderByDesc('latest_post_at')
    ->withCount(['posts AS approved_replies' => function ($query) {
        $query->where('posts.approved', true)
              ->where('posts.is_starting_thread', false);
    }])
    ->with(['posts' => function ($query) { // Posts
        $query->approved()
            ->with('user') // Author of post
            ->latest();
    }])
    ->with('user')
    ->paginate(20);

As I already wrote, both solutions seem to work, but do you think that one is better than another for some reason? Which would you prefer?

26 Dec
10 months ago

DoeJohn left a reply on Order By The Latest (most Recent) Related Model

@kfirba Hi, I've tried the following:

        $threads = Thread::join('posts as p', 'p.thread_id', '=', 'threads.id')
            ->where('forum_id', $this->forum->id)
            ->approved()
            ->withCount(['posts AS approved_replies' => function ($query) {
                $query->where('posts.approved', true)->where('posts.is_starting_thread', false);
            }])
            ->with(['posts' => function ($query) {
                $query->with('user')->approved()->latest();
            }])
            ->with('user')
            ->latest('p.created_at')
            ->paginate(20);

But the problem with this is that it now lists the same threads as many times as there are joined (related) posts (as there are posts with 'p.thread_id', '=', 'threads.id'). For example, if there is a thread with id=5 and there are 8 posts with thread_id=5, then this thread will be displayed 9 times.

24 Dec
10 months ago

DoeJohn started a new conversation Order By The Latest (most Recent) Related Model

I am using Laravel 5.4 and I have the following relations:

  • Forum hasMany Thread (threads())
  • Thread hasMany Post (posts())
  • Thread belongsTo User (user())
  • Post belongsTo User (user())

Currently, in my [email protected] I have the following:

    public function index()
    {
        $threads = $this->forum->threads()
            ->approved()
            ->withCount(['posts AS approved_replies' => function ($query) {
                $query->where('posts.approved', true)->where('posts.is_starting_thread', false);
            }])
            ->with(['posts' => function ($query) { // Posts
                $query->approved()
                    ->with('user') // Author of post
                    ->latest();
                }]
            )
            ->with('user') // Author of thread
            ->latest()
            ->paginate(20); 

        return view('forums.threads.index')->with([
            'forum' => $this->forum, 'threads' => $threads
        ]);
    }

My index.blade.php should show the listing of threads in which for each thread there will be:

  • its author (that's why I have ->with('user'))
  • number of replies (that's why I have >withCount(['posts AS approved_replies' => function ($query) { ...)
  • Date of the newest (latest) post and its author. That's why:
            ->with(['posts' => function ($query) { // Posts
                $query->approved()
                    ->with('user') // Author of post
                    ->latest(); // LATEST first
                }]
            )

... because then in index.blade.php I can access the latest post of each thread in the following way:

        @foreach ($threads as $thread) 
            {{ $thread->posts->first()->created_at; }}
            {{ $thread->posts->first()->user->username; }}
        @endforeach 

The problem with this code is that threads are sorted by their created_at, not by the most recent post. What I want to achieve is to order threads by the latest (most recent) post, but I don't know how to to this.

DoeJohn left a reply on Paginate Constrained Eager Load?

@Talinon Thank you very much!

22 Dec
10 months ago

DoeJohn started a new conversation Paginate Constrained Eager Load?

I have the following relations:

  • Forum hasMany Thread
  • Thread hasMany Post
  • Post belongsTo User

There is a page for listing of the forums and I have the following code:

        $forums = Forum::with(['threads' => function ($query) {   // Eager load latest threads
                $query->approved()->latest()
                    ->with(['posts' => function ($query) {   // Eager load latest posts
                        $query->with('user')->approved()->latest()->paginate(10);   //<---- PAGINATE
                }]);
            }])
            ->get();

As you can see, I eager-load threads with constraints in which I also eager-load posts (again with constraints) which are paginated.

Everything works OK except pagination of posts. The reason why I want to paginate is because I want to be able to use the paginator instance methods in order to be able to generate a link to the latest page (post) of thread.

But ->paginate(10) doesn't work, posts are not paginated but there are no errors. For example, in my controller if I do:

        foreach ($forums as $forum) {
            dd($forum->threads->first()->posts);
        }

... the result will not be LengthAwarePaginator but Collection. Am I doing something wrong or this isn't possible?

15 Dec
11 months ago

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait

Thank you very much. No more questions :)

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait

@bobbybouwmann Thank you!

And one more (final, I promise :-) ) question: Is it bad to put these methods in the Post model (instead of moving to a separate class/trait)? For example:

class Post extends Model 
{
    ...

    public function create($body, Thread $thread, User $user) {
        $this->body = $body;
        $this->thread_id = $thread->id;
        $user->posts()->save($this);
    }

    ...
}

DoeJohn left a reply on Laravel Controllers: Extract Code/Logic Into Trait

Thank you @bobbybouwmann .

You can create a specific class or service (PostService ) which can do this for you.

If I understood well, I can create a directory named Services, but I'm not sure which location would make more sense - in app/http or just in app? (I'm also wondering about Traits directory - where to put it, or maybe it is just a matter of preference?)

Also, as for thePostServiceclass - can you give me some starting points/tips. For example, would it extend Model. Would you create and implement interface? I'm not familiar with the Repository Pattern - but did you suggest that?

Or PostService class will be just a class in Services directory:

class PostService
{
    public function store($body, Thread $thread, User $user)  // not sure how to name 
    {
        $post = new Post();
        $post->body = $body;
        $post->thread_id = $thread->id;
        $user->posts()->save($post);
    }
}

... and that's all I need, no Repository Pattern, no implementing interfaces, no extending Model etc. ?

Finally, is it OK to name methods in this class as store, update, delete etc. (to be the same as in controller)?

Again, thanks a lot!

14 Dec
11 months ago

DoeJohn started a new conversation Laravel Controlers: Extract Code/Logic Into Trait

As @JeffreyWay wrote here:

Whenever you have a situation where you want two or more controller actions to use the same code/logic, that's an indication that you should extract that logic into its own class.

But, is it good to move it into Trait. I am creating some simple forum and this is my case:

  • I have two models: Thread and Post;
  • Thread has only title attribute, Post has only body attribute;
  • When a user wants to create (start) a new Thread - he will have to submit one form that contains two fields: title (thread title) and body (that will be the first post).

So, when someone creates a new thread, he also creates the first post of that thread.

This form (with tile and body fields) for starting a new thread is submitted to [email protected] where I do the following:

    public function store(Request $request)
    {
    // Store a newly created thread in storage.
        $thread = new Thread();
        $thread->title = $request->title;
        $thread = \Auth::user()->threads()->save($thread);

    // Store a newly post in storage.
        $post = new Post();
        $post->body = $request->body;
        $post->thread_id = $thread->id;
        \Auth::user()->posts()->save($post);

        return back();
    }

But, when someone replies (creates a new post within the thread) - he will have to submit a form that only contains body field, and that will be submitted to [email protected]:

    public function store(Request $request)
    {
    // Store a newly post in storage.
        $post = new Post();
        $post->body = $request->body;
        $post->thread_id = $thread->id;
        \Auth::user()->posts()->save($post);

        return back();
    }

As you can see - these two controllers are using the same code for storing a newly post in storage:

    // Store a newly post in storage.
        $post = new Post();
        $post->body = $request->body;
        $post->thread_id = $thread->id;
        \Auth::user()->posts()->save($post);

Is it good to move this code into a Trait? For example, in app/http I would create traits directory in which I would put PostsControllerTrait:

<?php

namespace App\Http\Traits;

use App\Post;
use App\Thread;
use Illuminate\Http\Request;

trait PostsControllerTrait
{
    /**
     * Store a newly created post in storage.
     *
     * @param  \Illuminate\Http\Request $request
     * @param Thread $thread
     * @return \Illuminate\Http\Response
     */
    public function storePost(Request $request, Thread $thread)
    {
        $post = new Post();
        $post->body = $request->body;
        $post->thread_id = $thread->id;
        \Auth::user()->posts()->save($post);
    }
}

Also, what do you think about the name PostsControllerTrait? The reason I have "Controller" in the name is because this trait will be used only in controllers. For me, PostsTrait looks like something related to Eloquent Modles.

Finally, what do you suggest - where to createtraits directory: in app/httpor just in ``app`?

16 May
1 year ago

DoeJohn started a new conversation Webpack: How To Setup Webpack-dev-server And Hot Reload [Webpack For Everyone]

Hi,

I was watching (Webpack for Everyone)[https://laracasts.com/series/webpack-for-everyone] series and now I would like to have Hot Relaoding when I run the npm run watch command. I would like it to work as it is working when using the vue-cli (when you run the npm run dev command).

So, I've installed webpack-dev-server and this what I've tried:

package.json:

{
  "name": "webpack-learning",
  "version": "1.0.0",
  "description": "",
  "scripts": {
    "dev": "cross-env NODE_ENV=development webpack",
    "watch": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
    "build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.24.1",
    "babel-loader": "^7.0.0",
    "babel-preset-es2015": "^6.24.1",
    "cross-env": "^5.0.0",
    "css-loader": "^0.28.1",
    "extract-text-webpack-plugin": "^2.1.0",
    "file-loader": "^0.11.1",
    "node-sass": "^4.5.2",
    "purifycss-webpack": "^0.6.2",
    "sass-loader": "^6.0.5",
    "style-loader": "^0.17.0",
    "webpack": "^2.4.1",
    "webpack-dev-server": "^2.4.5"
  }
}

If you look at the "watch" script - it uses webpack-dev-server and it also has --open --hot - this is the same as in vue-cli's package.json.

Also, here is my webpack.config.js:

var webpack = require('webpack');
var path = require('path');
const glob = require('glob');
const ExtractTextPlugin = require("extract-text-webpack-plugin");
const PurifyCSSPlugin = require('purifycss-webpack');
var inProduction = (process.env.NODE_ENV === 'production');

module.exports = {
    entry: {
        app: [
            './src/js/main.js',
            './src/sass/app.scss'
        ]
    },
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: '[name].js'
    },

    module: {
        rules: [
            {
                test: /\.s[ac]ss$/,
                use: ExtractTextPlugin.extract({
                    use: ['css-loader', 'sass-loader'],
                    fallback: 'style-loader'
                })
            },
            {
                test:  /\.(png|jpe?g|gif|svg|eot|ttf|woff|woff2)$/,
                loader: 'file-loader',
                options: {
                    name: '[name].[ext]?[hash]'
                }
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            },
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: "babel-loader"
            }
        ]
    },

    plugins: [
        new ExtractTextPlugin('[name].css'),

        new PurifyCSSPlugin({
            paths: glob.sync(path.join(__dirname, 'index.html')),
            minimize: inProduction
        }),

        new webpack.LoaderOptionsPlugin({
            minimize: inProduction
        })
    ],

    devtool: '#eval-source-map'
};

if (inProduction) {
    module.exports.devtool = '#source-map';

    module.exports.plugins.push(
        new webpack.optimize.UglifyJsPlugin({
            sourceMap: true,
            compress: {
                warnings: false
            }
        })
    );
}

Note that I'm using the ExtractTextPlugin (I have a separate CSS file), file-loader plugin (for url/images) etc. - this config is almost the same as Jeffrey created in his "Weback" series.

Finally, when I run the npm run watch command - there is no Hot Relaoding - nothing happens. There are no errors, but it doesn't do anything, it only opens index.html in the browser ( http://localhost:8080 ).

No changes are detected. For example, if I edit:

  • app.scss
  • index.html those changes are not loaded.

It doesn't even compile anything (if I remeber well, in vue-cli if you have deleted the dist directory - when you run npm run dev - it will re-create the dist directory again and it will put compiled assets there... but here, if you have deleted the compiled assets - it will not compile new ones).

25 Jan
1 year ago

DoeJohn left a reply on Appending Query Strings From Different Inputs (filters) In Blade View

Ok, I tried with those optional parameters in the route (instead of adding query strings):

Route::get('comments/{all?}/{latest?}', ['as' => 'comments.index', 'uses' => '[email protected]']);
  • all = show all comments (not just unpublished)
  • latest = order by created_at DESC

Now, my controller method has a lot of if-else checks:

    public function index(Request $request)
    {
        if ($request->latest) {
            if ($request->all) {
                $comments = Comment::orderBy('created_at', 'DESC')->paginate(15);
            } else {
                $comments = Comment::where('published', 0)
                    ->orderBy('created_at', 'DESC')
                    ->paginate(15);
            }
        } else {
            if ($request->all) {
                $comments = Comment::paginate(15);
            } else {
                $comments = Comment::where('published', 0)->paginate(15);
            }
        }

        return view('stadmin.comments.index', compact('comments'));
    }


AND it's even worse in my blade view:

    @if(request()->all)
        <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">All Comments
            <span class="caret"></span>
        </button>
        <ul class="dropdown-menu">
            @if(request()->latest)
                <li><a href="{{ route('comments.index', ['latest' => 'latest']) }}">Show Unpublished Only</a></li>
            @else
                <li><a href="{{ route('comments.index') }}">Show Unpublished Only</a></li>
            @endif
        </ul>
    @else
        <button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown">Unpublished
            <span class="caret"></span>
        </button>
        <ul class="dropdown-menu">
            @if(request()->latest)
                <li><a href="{{ route('comments.index', ['all' => 'all', 'latest' => 'latest']) }}">Show All Comments</a></li>
            @else
                <li><a href="{{ route('comments.index', ['all' => 'all']) }}">Show All Comments</a></li>
            @endif
        </ul>
    @endif
</div>
@if(request()->latest) Latest First @else Oldest First @endif

This is the code just for two simple dropdow filtes .... **This is terrible!**
There must be some better way... maybe without query strings/route params at all, maybe with Sessions? How would you do that?