artcore

artcore

Software Engineer at Self employed

Member Since 2 Years Ago

Experience Points
19,260
Total
Experience

740 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed
32
Lessons
Completed
Best Reply Awards
21
Best Reply
Awards
  • start your engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-in-session Created with Sketch.

    School In Session

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

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

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

  • subscriber Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • evangelist Created with Sketch.

    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 Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

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

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

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

Level 4
19,260 XP
Sep
20
2 days ago
Activity icon

Replied to Where Would You Save Tons Of Images That Are About 40kb Small? File, Block Or Object Storage?

Especially now with Http/2 I never worry about cdn or offloading images. The most delays for me are ironically from google fonts but "rel=prefetch" helps

https://en.wikipedia.org/wiki/HTTP/2#Differences_from_HTTP_1.1

Sep
14
1 week ago
Activity icon

Replied to How To Add Scale In Laravel Image Intervention Package?

@jonjie If you add the needed dom elements (eg div id=image) and import the cropperjs script, above will guide you to the solution for both frontend and backend. Granted it's a bit fragmented but the cropperjs docs are a good starting point also.

https://fengyuanchen.github.io/cropperjs/ -> examples

Start with an image element, initialize cropperjs on it (see my example in init()) and you will see what my fragments are meant to do.

I'm one of those guys who don't use github and fiddles ;)

Activity icon

Replied to How To Add Scale In Laravel Image Intervention Package?

@jonjie Here's a short version of my javascript

let cropper;

const fileInput  = document.querySelector('input[name="document[file]"]'),
      image      = document.getElementById('image'),
      input      = document.querySelector('input[name="document[crop]"]');

fileInput.addEventListener('change', e =>
{
  const file      = e.target.files[0];


  //send the uploaded file to the cropbox img src
  image.innerHTML = `<img alt="" src="${URL.createObjectURL(file)}">`;

  //I left out some things but you get the idea
  init(options.width, options.height);

});

const init = (width, height) =>
{
  if (cropper instanceof Cropper)
  {
    const parent = document.querySelector('.cropper-container');
    if (parent)
      parent.remove();//bug in cropper github#1255
    cropper.destroy();
  }

  cropper = new Cropper(image.querySelector('img'),
    {
      dragMode:           'move',
      minContainerWidth:  image.style.width,
      minContainerHeight: image.style.height,
      minCropBoxWidth:    width,
      minCropBoxHeight:   height,
      wheelZoomRatio:     0.05,
      autoCropArea:       0.65,
      guides:             false,
      background:         false,
      highlight:          false,
      movable:            true,
      cropBoxResizable:   false,
      cropBoxMovable:     false,
      ready:              () =>
                          {
                            set() //I do this elsewhere IF something got changed i.e. cropped, zoomed otherwise just send the original upload to the backend
                          }
    });

};

const set = () =>
{
  const cropped = cropper.getCroppedCanvas(
    {
      width:                 options.width,
      height:                options.height,
      fillColor:             mimeType.value === 'image/png' ? '#FFF0' : '#FFFFFF',
      imageSmoothingEnabled: true,
      imageSmoothingQuality: 'high'
    });

  if (cropped)
    input.value = cropped.toDataURL(mimeType.value || 'image/png', 100);

};

You will get a base64 encoded string in your backend OR without cropping possibly a regular file upload

Here's part of my backend to handle that

$file = $this->document['crop']; //this is the base64 encoded string from toDataURL set by cropperjs

    if (!$file) //regular upload possibly
    {
      $file = $this->document['file'];
      if ($file instanceof UploadedFile)
        $this->source = $file->get();
    }
    else
    {

      $mimeType = $this->document['mime_type'] ?? 'image/jpeg';
      if ($mimeType === 'image/svg+xml')
        $mimeType = 'image/png'; //toDataURL falls back to png | only raster, no vector
      
      $this->source = base64_decode(str_replace("data:$mimeType;base64,", '', $file));
    }

I added some comments to help you understand

I validate mimeType either from UploadedFile or like this:

$source = getimagesizefromstring($this->source);

Never trust user input :)

===

Some DOM elements

<label class="my-20"><input type="file" name="document[file]"></label>


<div id="image">
      <img src="placeholder.jpg" alt="choose file" title="choose file">
</div>

Hope you get the idea - my full code is too elaborate to post ;)

Sep
13
1 week ago
Activity icon

Replied to How To Add Scale In Laravel Image Intervention Package?

I'd say drop Quillotine and use cropperjs. https://fengyuanchen.github.io/cropperjs/ For resizing I'm using the php builtin gd library and it works flawlessly using mostly https://www.php.net/manual/en/function.imagewebp.php since I only store webp (if not svg and > 320 wide)

Activity icon

Replied to Great Coding Fonts

That's what I was gonna say. Love the ligatures ;)

Sep
12
1 week ago
Activity icon

Replied to What Do You Think About Laravel 8

@cotiga I'm glad to hear someone else say what I've been thinking... tailwind in a mess of inline classes! What happened to the cascading part of Ccs? Unless I'm missing something ;) Vanilla rules!

Sep
11
1 week ago
Activity icon

Replied to Laravel 7 Vs Laravel 8 Dilemma

The best thing about Laravel is that literally everything is optional. You don't have to use Eloquent, vue, react, livewire, laravel-mix, bootstrap, tailwind, auth system and what not.

Just plain decent PHP But I do recommend to use the brilliant Blade templating system and learn how to use ServiceProviders along with the App\Container intricacies.

Ah and of course Artisan. Or maybe you prefer the WP-CLI and open up your server to billions of attacks ;)

Just my 2c. Please give it anyother shot. In the long run you will be greatful. I know I am!

Activity icon

Replied to PHPStorm: Impossible To Connect To Mysql DB.

You need to enable remote access IF your db is on a remote server.

GRANT ALL PRIVILEGES ON `your_database` TO 'username'@'IP' IDENTIFIED BY 'password';

and also comment out #bind-address = 127.0.0.1 in your mysql cnf

the latter means only accept connections from localhost which you don't want.

Sep
10
1 week ago
Activity icon

Replied to Laravel 7 Vs Laravel 8 Dilemma

Looks like 6 is the current LTS which I usually follow but L7 has a faster router so that's what I'm using atm for all my projects.

https://en.wikipedia.org/wiki/Laravel

Sep
09
1 week ago
Activity icon

Replied to How To Output Last 3 Digits From Number?

If memory serves me it's substr( start, length) so I would do:

{{ substr($order->order_id ?? '000', 0, -3) }}

Also the null coalesce inside the function as it would still give an exception your way.

Sep
07
2 weeks ago
Activity icon

Replied to Global Variable Locale For Male Female

Is gender in your users table?

If so you should be able to do auth()->user()->gender; Or request()->user()->gender should also work.

Sep
05
2 weeks ago
Activity icon

Replied to Remove Element By Id

remove the apostrophy here

'${id}'

to just

${id}

In es6 string literal between backticks you don't use extra quotes or apostrophies

Also simply use id in method parameters -> remove(id) NOT remove(${id})

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals

Sep
04
2 weeks ago
Activity icon

Replied to Wat Is Jullie Motivatie Om Laravel Te Leren?

I know the following will make me sound like a d!ck ;) Using correct grammar will make you smarter and since we're using correct grammar in our coding, why not in our speech?

en ZIJ werken met Laravel 'hun' is bezittelijk of meewerkend voornaamwoord. Het is HUN boek, het boek is van HEN. ZIJ hebben een boek.

sorry, had to say it ;)

Activity icon

Replied to How Use Javascript For Img Src Base64 Encode ?

WebP official support has been added in Safari 14 https://developer.apple.com/documentation/safari-release-notes/safari-14-beta-release-notes#Media

Just in time. I just rewrote my image system to just generate webp source sets ;)

Sep
03
2 weeks ago
Activity icon

Awarded Best Reply on L7 - Invoking A Controller To Get Data If Clicked On A Tab

Route::get("/tab", "ActionTabController");

__invoke is called automagically if no method is given in the route

You can use Dependency Injection if needed

class ActionTabController

public function __invoke(DI $something) 
{
  //... return response. possibly a blade partial to load in your tab
}

I assume you know how to handle a click event to trigger the route action :)

BTW I named these kinds of routes Actions as a personal preference. So I would name the "controller"

class SettingsTabAction

Sep
01
3 weeks ago
Activity icon

Replied to L7 - Invoking A Controller To Get Data If Clicked On A Tab

I assumed you're using javascript + ajax,

Cheers

Activity icon

Replied to L7 - Invoking A Controller To Get Data If Clicked On A Tab

Route::get("/tab", "ActionTabController");

__invoke is called automagically if no method is given in the route

You can use Dependency Injection if needed

class ActionTabController

public function __invoke(DI $something) 
{
  //... return response. possibly a blade partial to load in your tab
}

I assume you know how to handle a click event to trigger the route action :)

BTW I named these kinds of routes Actions as a personal preference. So I would name the "controller"

class SettingsTabAction

Aug
28
3 weeks ago
Activity icon

Replied to How To Host A Laravel Application At A Route Of Another Laravel Application?

I have several staging domains on one laravel instance.

Create a public folder and add index.php with path to autoload of the main app. Doing a bit of magic with middleware and config setting on runtime by checking the domain and voila.

I change database, domain cookie and static files directory via middleware and also add the logged in user to the request user resolver. Sounds involved but it's fairly straightforward.

As for scalability, it's still one laravel app but with multiple DBs. Should be able to run in a cloud

Aug
27
3 weeks ago
Activity icon

Replied to Issue With Field Injection When Validating Arrays

Perhaps

$data = array_filter($array, function($item)
{
   $valid = in_array($key = key($item), ['name', ...goodguys]);

   if(!$valid) unset($item[$key]);

   return $item;
});

But I'm sure a more elegant way will come up ;)

Aug
23
4 weeks ago
Activity icon

Replied to Comments By Paragraph

https://editorjs.io/

also stores paragraphs and other blocks in a json object which you can save to DB entirely or per block.

And no dependencies.

When you edit the resource you can check if any #id has a comment to prevent deletion for instance.

I have it live on one app and seems to be working nicely minus a few quirks that will be solved soonish it looks. (alignment of paragraphs e.g.) plus there's a package if you happen to use Nova

Aug
22
1 month ago
Activity icon

Replied to Linting And Beautifying With Sublime

PhpStorm! https://www.jetbrains.com/phpstorm/ Also (psr) formatting, auto-complete / hinting, linting for pretty much every language. And best feature: auto save on blur ;)

Really, out of the box the way it should work. (no affiliation just a happy user since 2016)

Aug
18
1 month ago
Activity icon

Replied to Laravel Setup On ChromeOS

I have a dual boot on my chromebook with linux arm. Running phpstorm and all like a breeze. Do you have arm64 or the other one?

https://archlinuxarm.org/ All you do is disable OS verification and install linux on a SD card. Finding the right card took me 3 tries though lol.

Aug
16
1 month ago
Activity icon

Replied to Lumen To Create Site?

Laravel and WordPress comparison is like comparing a grocery bag full of ingredients to a ready made pizza Cook it yourself and stay away from pizza ;) Unless you need to serve dinner in 5 minutes. There's always a but.

I used to have a Lumen install with all missing Laravel components such as sessions and some blade directives (@csrf and a few more). Works well and blazing fast. However Laravel 7 has an optimized router that is almost or just as fast as FastRoute used by Lumen. Combined with disabling classes and middleware in your config makes Laravel plenty fast. TTFB < 50ms on a live dedicated server

In short Lumen is for APIs or stateless apps -> no sessions/cookies as it was intended (after version 5.1)

Aug
15
1 month ago
Activity icon

Replied to Secure Computer

If you're suspicious you can track outgoing traffic with wireshark or command line using netstat (= Linux but Macs are based on that I believe). And never run your day to day OS as a user with admin privileges I'd say.

I once (years ago) found ftp logins that were leaked by filezilla storing credentials in plain text. Ever since I recommend winSCP for windows and sftp with keys for anyone and possibly restrict server access by IP (ssh)

Aug
02
1 month ago
Activity icon

Replied to What's The Most Recommended Date And Time Picker To Use For Web And Mobile App?

@chron I didn't get a notification of your reply...

I use flatpickr but so far only for dates I'll play with the time picker tomorrow. You you want it to be hidden by default or have 00:00 as default or another time?

Jul
31
1 month ago
Activity icon

Replied to What's The Most Recommended Date And Time Picker To Use For Web And Mobile App?

I'm happy with https://flatpickr.js.org/ No dependencies like jQuery or Moment(for date conversions)

Has min max dates and also range...

By default flatpickr disables on mobile saying the native interface is probably better. But you can easily enable the UI again.

Jul
18
2 months ago
Jul
17
2 months ago
Activity icon

Replied to Preventing A Post From Being Created In Case A Validation Fails

I haven't used the laravle way in a long time but the validator relies on a request. Doing request()->validate or $this->validate does not inject the current request

public function store(Request $request)
{
    $validatedData = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
    ]);

    // The blog post is valid...
}


$this->validate($request, [//now with Illuminate\Http
            'title' => 'required',
            'body' => 'required',
        ]);
Jul
16
2 months ago
Activity icon

Replied to How Can I Obfuscate The Codes In My Laravel App?

https://phpbolt.com/

I had this bookmarked but never used it. It's free! Good luck

Jul
11
2 months ago
Activity icon

Replied to Jquery To Fetch JS Post

@jlrdw That was my intention ;) I do send a 422 response from my validator but I assume Laravel does the same. In any case it's handier to have the error bags as key value so you can show the error message in the right spot based on input[name] which works a treat with my edit. I'm sure you can use Laravel's validation rules as usual, but it throws just an indexed array so all you can do is slap it in a single div, bunching all errors.

Activity icon

Awarded Best Reply on Jquery To Fetch JS Post

I found it's generally best to let the browser figure out the content type and use formData() in stead of stringifying an data object

Here are my working parts.

class HttpProvider
{
  constructor(method = 'GET')
  {
    this.method = method;
    this.headers = {};
    this.credentials = null;
    this.body = null;
  }

  request(url)
  {
    return new Promise((resolve, reject) =>
    {
      fetch(url, this.options())
        .then(response =>
        {
          if (response.ok || response.status === 422)
            response.json()
                    .then(data => resolve(data))
                    .catch(error => console.error("Fetch Load Error - Invalid JSON returned", error));
          else
            console.error("Fetch Load Error - Connection Error: " + response.status, response.statusText);
        })
        .catch(error => console.error("Fetch Load Error - Connection Error: ", error));
    });
  }

  options()
  {
    const csrf        = document.querySelector('meta[name=csrf-token]'),
          contentType = this.body ? "multipart-form-data" : "application/json",//application/x-www-form-urlencoded
          headers     =
            {
              // "Content-Type":     contentType,
              // Accept:             'application/json',
              "X-Requested-With": 'XMLHttpRequest',
              'X-CSRF-TOKEN':     csrf ? csrf.getAttribute('content') : null
            };

    let config = {
      method:      this.method,
      credentials: this.credentials ? this.credentials : 'include',
      headers:     this.headers ? Object.assign(headers,this.headers) : headers

      //mode: "cors", // no-cors, cors, *same-origin
      //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      //redirect: "follow", // manual, *follow, error
      //referrer: "no-referrer", // no-referrer, *client
    };

    if (this.method !== 'GET')
      config.body = this.body;//JSON.stringify

    return config;
  }
}

window.Http = new HttpProvider();

export default window.Http;

Implementation

button.addEventListener('click', e =>
  {

    Array.from(document.querySelectorAll('.text-error')).map(error =>
    {
      error.remove();
    });

    const formData = new FormData(),
          inputs   = form.querySelectorAll('[name]');


    for (const input of inputs)
    {
      if ((input.type === 'checkbox' || input.type === 'radio') && !input.checked) continue;

      formData.append(input.name,
        input.type === 'file'
          ? input.files[0]
          : input.value);
    }

    const id  = formData.get('id'),
          url = form.getAttribute('action');


    Http.method = 'POST';
    if (id)
      formData.append('_method', 'PUT');

    Http.body = formData;


    Http.request(url).then(response =>
    {
      if (!response.hasOwnProperty('redirect'))
      {
        if (Array.isArray(response))
          messages.innerHTML = `<column class="base-50 error p-5 my-20 dismiss">${response.toString().replace(/\.,/g, '<br>')}</column>`;

        for (const input of inputs)
        {
          const dotNotation = input.name.replace(/[\[\]\[?]/g, '.')
                                .replace('..', '.');//clean up regex

          let lastDotIndex = dotNotation.lastIndexOf('.'),
              fieldLength  = dotNotation.length;

          if (fieldLength - 1 === lastDotIndex)
            fieldLength = lastDotIndex

          const index = dotNotation.substr(0, fieldLength);

          if (response.hasOwnProperty(index))
            input.insertAdjacentHTML('afterend', `<span class="text-error">${response[index]}</span>`);
        }
      }
      else
      {
        messages.innerHTML = `<column class="dismiss">${response.message}</column>`;

        if (response.hasOwnProperty('redirect') && typeof response.redirect === 'string')
          window.location.href = `${window.location.origin}/${response.redirect}`;
        else if (response.hasOwnProperty('disable') && response.disable)
          form.remove();
        else if (response.redirect)
          window.history.go(-1);
      }
    });
  });

For adding errors I'm using a custom validator that returns the input fields in dot notated key values

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\Validator;

class ValidateJob
{
  
  /**
   * @var Request $request
   */
  protected $request;
  public    $connection;
  protected $messages         = [];
  protected $customAttributes = [];
  
  
  public function __construct($request, $connection = "core.database.default")
  {
    $this->request    = $request;
    $this->connection = $connection;
  }
  
  
  protected function rules()
  {
    return [];
  }
  
  
  public function handle()
  {
    /**
     * @var Validator $validator
     */
    $validator = app(\Illuminate\Contracts\Validation\Factory::class)
      ->make($this->request->input(), $this->rules(), $this->messages, $this->customAttributes);
    
    if ($validator->fails())
      throw new ValidationException($validator, new JsonResponse(
        array_combine($validator->errors()->keys(),$validator->errors()->all()), 422));
  }

That will auto append/remove the errors under the input fields.

Example of controller method

public function store(Request $request)
  {
    $this->dispatchNow(new ValidateOrderJob($request));
    $id = $this->dispatchNow(new StoreOrderJob($request->input()));
    
    return $this->dispatchNow(new RespondWithJsonOrRedirectJob($request,
      [
        'message'  => __('Successfully Stored!'),
        'id'       => $id,
        'redirect' => true,
      ]));
  }

Hope it helps put you on the right path. I know it's an opinionated implementation ;)

One fetch() quirk is that have to implement your own abort which I haven't done yet.

https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort

Jul
10
2 months ago
Activity icon

Replied to Jquery To Fetch JS Post

I found it's generally best to let the browser figure out the content type and use formData() in stead of stringifying an data object

Here are my working parts.

class HttpProvider
{
  constructor(method = 'GET')
  {
    this.method = method;
    this.headers = {};
    this.credentials = null;
    this.body = null;
  }

  request(url)
  {
    return new Promise((resolve, reject) =>
    {
      fetch(url, this.options())
        .then(response =>
        {
          if (response.ok || response.status === 422)
            response.json()
                    .then(data => resolve(data))
                    .catch(error => console.error("Fetch Load Error - Invalid JSON returned", error));
          else
            console.error("Fetch Load Error - Connection Error: " + response.status, response.statusText);
        })
        .catch(error => console.error("Fetch Load Error - Connection Error: ", error));
    });
  }

  options()
  {
    const csrf        = document.querySelector('meta[name=csrf-token]'),
          contentType = this.body ? "multipart-form-data" : "application/json",//application/x-www-form-urlencoded
          headers     =
            {
              // "Content-Type":     contentType,
              // Accept:             'application/json',
              "X-Requested-With": 'XMLHttpRequest',
              'X-CSRF-TOKEN':     csrf ? csrf.getAttribute('content') : null
            };

    let config = {
      method:      this.method,
      credentials: this.credentials ? this.credentials : 'include',
      headers:     this.headers ? Object.assign(headers,this.headers) : headers

      //mode: "cors", // no-cors, cors, *same-origin
      //cache: "no-cache", // *default, no-cache, reload, force-cache, only-if-cached
      //redirect: "follow", // manual, *follow, error
      //referrer: "no-referrer", // no-referrer, *client
    };

    if (this.method !== 'GET')
      config.body = this.body;//JSON.stringify

    return config;
  }
}

window.Http = new HttpProvider();

export default window.Http;

Implementation

button.addEventListener('click', e =>
  {

    Array.from(document.querySelectorAll('.text-error')).map(error =>
    {
      error.remove();
    });

    const formData = new FormData(),
          inputs   = form.querySelectorAll('[name]');


    for (const input of inputs)
    {
      if ((input.type === 'checkbox' || input.type === 'radio') && !input.checked) continue;

      formData.append(input.name,
        input.type === 'file'
          ? input.files[0]
          : input.value);
    }

    const id  = formData.get('id'),
          url = form.getAttribute('action');


    Http.method = 'POST';
    if (id)
      formData.append('_method', 'PUT');

    Http.body = formData;


    Http.request(url).then(response =>
    {
      if (!response.hasOwnProperty('redirect'))
      {
        if (Array.isArray(response))
          messages.innerHTML = `<column class="base-50 error p-5 my-20 dismiss">${response.toString().replace(/\.,/g, '<br>')}</column>`;

        for (const input of inputs)
        {
          const dotNotation = input.name.replace(/[\[\]\[?]/g, '.')
                                .replace('..', '.');//clean up regex

          let lastDotIndex = dotNotation.lastIndexOf('.'),
              fieldLength  = dotNotation.length;

          if (fieldLength - 1 === lastDotIndex)
            fieldLength = lastDotIndex

          const index = dotNotation.substr(0, fieldLength);

          if (response.hasOwnProperty(index))
            input.insertAdjacentHTML('afterend', `<span class="text-error">${response[index]}</span>`);
        }
      }
      else
      {
        messages.innerHTML = `<column class="dismiss">${response.message}</column>`;

        if (response.hasOwnProperty('redirect') && typeof response.redirect === 'string')
          window.location.href = `${window.location.origin}/${response.redirect}`;
        else if (response.hasOwnProperty('disable') && response.disable)
          form.remove();
        else if (response.redirect)
          window.history.go(-1);
      }
    });
  });

For adding errors I'm using a custom validator that returns the input fields in dot notated key values

use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use Illuminate\Validation\Validator;

class ValidateJob
{
  
  /**
   * @var Request $request
   */
  protected $request;
  public    $connection;
  protected $messages         = [];
  protected $customAttributes = [];
  
  
  public function __construct($request, $connection = "core.database.default")
  {
    $this->request    = $request;
    $this->connection = $connection;
  }
  
  
  protected function rules()
  {
    return [];
  }
  
  
  public function handle()
  {
    /**
     * @var Validator $validator
     */
    $validator = app(\Illuminate\Contracts\Validation\Factory::class)
      ->make($this->request->input(), $this->rules(), $this->messages, $this->customAttributes);
    
    if ($validator->fails())
      throw new ValidationException($validator, new JsonResponse(
        array_combine($validator->errors()->keys(),$validator->errors()->all()), 422));
  }

That will auto append/remove the errors under the input fields.

Example of controller method

public function store(Request $request)
  {
    $this->dispatchNow(new ValidateOrderJob($request));
    $id = $this->dispatchNow(new StoreOrderJob($request->input()));
    
    return $this->dispatchNow(new RespondWithJsonOrRedirectJob($request,
      [
        'message'  => __('Successfully Stored!'),
        'id'       => $id,
        'redirect' => true,
      ]));
  }

Hope it helps put you on the right path. I know it's an opinionated implementation ;)

One fetch() quirk is that have to implement your own abort which I haven't done yet.

https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort

Activity icon

Replied to Multiple Databases In Laravel.

In my case the server will squawk at 50+ users (heavy app with millions of entries for each user) so I just get another box and deploy again.

Jul
02
2 months ago
Activity icon

Replied to Very High TTFB When Fetching 1000 Records With Relations

Without sending a response it would still create a 1000 Models right? Why not do 2 or 3 queries and map them yourself as pure Objects? By using PDO fetchUnique and fetchGroup for many-to-many's all you do is map the primary keys with an array lookup. Very very fast. I can send 400k records hydrated with relations to the browser. (sometimes handy to have a single call and do processing via js in the browser - no more server requests...)

Jun
28
2 months ago
Activity icon

Replied to Is There A Way To Validate A Form As A Group?

If you want to bunch some variables together, why not use

<input name="account[name]">
<input name="account[age]">
<input name="account[role]">

Validation:

['account.* => 'required, ...']
Activity icon

Replied to Save Visitor Ip In Database.

I don't know why you want to track your visitors' IPs but I tried for a custom analytics solution on sites with 1000s of daily hits. Conclusion:You can't track visitors by IP - highly unreliable. Use cookies (with consent).

Why? Mobile ISPs assign IPs round robin out of their pool every time you (re)connect to the network. And that's 60-70% of the traffic.

Jun
22
3 months ago
Activity icon

Replied to Is There A Way To Show Content When All The Css And Js Are Done Loading

Ah that's what you meant ;)

I can think of two solutions:

  • use the markup from the package (css classes and html markup) for the element in question.
  • hide the elements in question, so they show up with their final design

For the latter you could add a css class "hidden" and remove it after the DOM is ready and js parsed

Jun
21
3 months ago
Activity icon

Replied to Is There A Way To Show Content When All The Css And Js Are Done Loading

I think I misunderstood your question earlier. You want to show a loader until the page is done loading? Can you elaborate a bit more?

Usually the loader will instantly and you kill it when all is ready to go. Are you saying the loader is not removed in your "load" event?

Jun
11
3 months ago
Activity icon

Replied to Designing A Large Project - Specifically Keep Controllers Short

@chris_j for instance a form post will always have a few steps to go through. Validate, process and return a finished action; redirect or display a message. If you're more comfortable to have these steps in a dedicated class, why not, but you are adding unnecessary complexity imho. In my case the controller is still unaware and has no other responsibility than to start the jobs/commands. Take a look at ADR also - action domain response. This is probably the leanest way but adds complexity because of the sheer number of classes you'd have to deal with along with writing custom routes (Laravel uses controller actions by default)

Now that I think of it, I read this first on a blog by @martinbean :)

https://martinbean.dev/blog/2016/10/20/implementing-adr-in-laravel/ and he has a few more nice posts.

Jun
10
3 months ago
Activity icon

Replied to Designing A Large Project - Specifically Keep Controllers Short

I'm enjoying command bus pattern. Reusable jobs keep the controller lean and you can queue jobs too, so some parts are async

two methods in my Document resource controller

public function index(Request $request)
  {
    if ($request->isXmlHttpRequest())
      return response()->json(['data' => $this->dispatchNow(new IndexDocumentJob($request->query()))]);
    
    return view('document::index');
  }


public function store(Request $request)
  {

//throws exception with error bags
    $this->dispatchNow(new ValidateDocumentJob($request));

//store the file
    $this->dispatchNow(new MoveDocumentJob($request));

//store in db
    $id = $this->dispatchNow(new StoreDocumentJob($request->input()));

//delete unlinked files  
    $this->dispatch(new StorageMaintenanceJob(new IndexDocumentJob()));//async
   
//json response or a redirect 
    return $this->dispatchNow(new RespondWithJsonOrRedirectJob($request,
        [
          'message'  => __('Successfully Stored!'),
          'id'       => $id,
          'redirect' => true,
        ]));
  }

The update method is similar

Activity icon

Replied to How To Convert View File In Image And Return In Api In Laravel

You can simple return a string which will be the response for your ajax call

return view('template1')->render();
Activity icon

Replied to How Can I Count Fileextention From This Array

array_count_values(array_column($array, 'fileextension'));

/*
example output
[
  'pdf' => 5,
  'jpeg'=>3,
]

*/

Jun
07
3 months ago
Activity icon

Awarded Best Reply on JQuery Guillotine And Laravel

It's 5 years old, I suggest https://fengyuanchen.github.io/cropperjs/

Activity icon

Replied to How To Add Class After Upload An Image?

document.querySelector('img').classList.add('rounded-circle');

Isn't a circle always round ;)

Jun
06
3 months ago
Activity icon

Replied to What Is The Best IDE For Laravel?

How is PHPStorm expensive? I'm paying like 60$ a year. Started at $80 I believe but each year you get a little discount. It's the one editor that is actually never in your way and the auto code formatting is prefect and the auto upload on blur is super handy. Of course the hinting and builtin DataGrip and everything else is just a must have. --my opinion!

Sublime Text is great for its speed but if you're like me and need the code blocks nicely aligned you end up doing hundreds of keystrokes extra a session, tabbing everything in place ;)