Add avatar to your model/resource

Draft Disclaimer: Please note that this article is currently in draft form and may undergo revisions before final publication. The content, including information, opinions, and recommendations, is subject to change and may not represent the final version. We appreciate your understanding and patience as we work to refine and improve the quality of this article. Your feedback is valuable in shaping the final release.

Language Mismatch Disclaimer: Please be aware that the language of this article may not match the language settings of your browser or device.
Do you want to read articles in English instead ?

Add avatar to your model/resource

<?php

namespace App\Models\Concerns;

use Illuminate\Support\Str;
use Illuminate\Http\UploadedFile;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * Trait HasAvatar *
 * @package App\Models\Concerns
 *
 * Expected props on model
 *
 * @property string avatar
 * @property string name
 */
trait HasAvatar
{
  /**
   * Initialize the trait.
   */
  public function initializeHasAvatar(): void
  {
    $this->mergeFillable(['avatar']);
  }

  /**
   * Prepare the given value for storage.
   */
  public function setAvatarAttribute($value): void
  {
    $this->setAvatar($value);
  }
  /**
   * Prepare the given value for storage for a certain avatar.
   */
  public function setAvatar($value): self
  {
    if ($value instanceof UploadedFile) {
      $name = time() . '_' . $value->getClientOriginalName();
      $path = $this->avatarDirectory();
      Storage::disk('avatars')->putFileAs($path, $value, $name);
      $this->attributes['avatar'] = $name;

      // remove previous avatar if any
      if (isset($this->original['avatar'])) {
        $this->removeAvatarFile($this->original['avatar']);
      }
    } else {
      // This is only here for seeding purposes
      $this->attributes['avatar'] = $value;
    }

    return $this;
  }

  /**
   * The avatar url.
   */
  public function avatarUrl(): ?string
  {
    if ($this->avatarIsSet()) {
      return Storage::disk('avatars')->url($this->avatarPath());
    }

    if (! $this->hasGravatar) {
      return $this->defaultAvatarUrl();
    }

    $avatar = rescue(
      fn () => Gravatar::exists($this->email) ? $this->gravatarUrl() : false,
      $this->defaultAvatarUrl()
    );

    return $avatar ?: $this->defaultAvatarUrl();
  }

  protected function avatarIsSet()
  {
    return isset($this->attributes['avatar']) && $this->attributes['avatar'];
  }

  /**
   * Used for making front-end decisions about displaying Avatar
   */
  public function hasAvatar(): bool
  {
    if ($this->avatarIsSet()) {
      return true;
    }

    return ! Str::of($this->avatarUrl())->startsWith('https://ui-avatars.com/api/');
  }

  /**
   * Get gravatar url from email if exists. Otherwise will use default fallback
   */
  public function gravatarUrl(): string
  {
    return Gravatar::get($this->email);
  }

  /**
   * Avatar directory.
   */
  public function avatarDirectory(): string
  {
    return $this->getMorphClass();
  }

  /**
   * Avatar path.
   */
  public function avatarPath(): string
  {
    return $this->avatarDirectory() . '/' . $this->original['avatar'];
  }

  /**
   * Boot with Mode, register events.
   */
  protected static function bootHasAvatar(): void
  {
    static::updating(function (Model $entity) {
      // Only remove the older avatar if it has changed
      collect($entity->getAttributes())->each(function ($value, $attribute) use ($entity) {
        if (! isset($entity->original[$attribute])) {
          return;
        }

        if (! Storage::disk('avatars')->exists($entity->avatarPath($attribute)) || ! Storage::disk('avatars')->size($entity->avatarPath($attribute))) {
          return;
        }

        // Property is for file, check if it has changed
        if ($entity->original[$attribute] != $entity->attributes[$attribute]) {
          $entity->removeAvatarFile($entity->original[$attribute]);
        }
      });
    });

    static::deleting(function (Model $model) {
      if (in_array(SoftDeletes::class, class_uses_recursive($model))) {
        if (! $model->forceDeleting) {
          return;
        }
      }

      $this->removeAvatarFile($this->original['avatar']);
    });
  }

  /**
   * Default avatar url.
   */
  protected function defaultAvatarUrl(): string
  {
    return 'https://ui-avatars.com/api/?background=random&name=' . $this->name;
  }

  public function getAvatarUrlAttribute()
  {
    return $this->avatarUrl();
  }

  /**
   * Remove the avatar file.
   */
  public function removeAvatarFile(string $avatar = null): void
  {
    if (is_null($avatar)) {
      $avatar = $this->original['avatar'];
    }

    Storage::disk('avatars')->delete($this->avatarDirectory() . '/' . $avatar);
  }
}

  • config/filesystems -> disks
'avatars' => [
    'driver' => 'local',
    'root' => storage_path('app/public/avatars'),
    'url' => env('APP_URL') . '/storage/avatars',
    'visibility' => 'public',
],
  • clear command
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;

class AvatarClear extends Command
{
  /**
   * The name and signature of the console command.
   *
   * @var string
   */
  protected $signature = 'avatar:clear';

  /**
   * The console command description.
   *
   * @var string
   */
  protected $description = 'Clear avatars directory';

  /**
   * Execute the console command.
   *
   * @return int
   */
  public function handle()
  {
    $files = Storage::disk('avatars')->allFiles();

    $this->info('Avatars clearing...');

    Storage::disk('avatars')->delete($files);

    $this->info('Avatars cleared.');

    return self::SUCCESS;
  }
}
  • use
use HasAvatar;