newRelatedInstance(static::class); $foreignPivotKey = 'relation_id'; $relatedPivotKey = 'image_id'; $table = 'associate_images'; $name = 'relation'; $morph = new MorphToMany( $instance->newQuery(), $class, $name, $table, $foreignPivotKey, $relatedPivotKey, $class->getKeyName(), $instance->getKeyName(), $relationName, false ); $morph->withPivot('position'); return $morph; } /** * */ protected static function boot(): void { parent::boot(); static::creating(function (Image $model) { if (empty($model->content)) { throw new InvalidContent("Content must be defined to save image"); } if (!($model->content instanceof InterventionImage)) { throw new InvalidContent("Content must be an instance of Intervention\Image"); } $model->savePicture(); }); static::updating(function (Image $model) { $model->savePicture(); }); static::deleted(function (Image $model) { $model->deletePicture(); }); } protected function savePicture() { if (empty($this->content)) { return true; } $this->generateFilename(); return $this->saveContent(); } /** * @param boolean $force */ public function generateFilename($force = false): void { if ($this->filename && !$force) { return; } $this->filename = self::generateRandomFilename(); } /**+ * @return string */ public static function generateRandomFilename(): string { return mb_strtolower(Str::random(60)); } /** * @return InterventionImage */ protected function saveContent(): InterventionImage { if (empty($this->content)) { throw new \InvalidArgumentException("Content is Empty"); } $path = $this->getPath(); $dir = dirname($path); if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) { throw new \RuntimeException(sprintf('Directory "%s" was not created', $dir)); } $res = $this->content->save($path); app(OptimizerChain::class)->optimize($path); return $res; } /** * @return string|null */ public function getPath(): ?string { if (empty($this->filename)) { return null; } return self::getAbsolutePath($this->filename); } /** * @param $filename * @return string */ public static function getAbsolutePath($filename): string { $basePath = config('image.path'); if (empty($basePath)) { throw new RuntimeException('You must defined config image.path'); } $parts = array_slice(str_split(md5($filename), 2), 0, 2); return $basePath . '/' . implode('/', $parts) . '/' . $filename; } /** * @return bool */ protected function deletePicture(): bool { $path = $this->getPath(); if (is_file($path)) { return unlink($path); } return true; } /** * @return string */ public function getUrlAttribute(): ?string { $route = config('image.route'); if (empty($route)) { return null; } return app('url')->to('/' . $route . '/' . $this->filename); } /** * @param string $value */ public function setFilenameAttribute(string $value): void { $pattern = '/[^a-z_\-\.0-9]/i'; $patternLetter = '/[a-z0-9]+/i'; if (preg_match($pattern, $value) || !preg_match($patternLetter, $value)) { throw new \InvalidArgumentException("Invalid filename. Must be only composed only with a-z, A-Z, 0-9 and dot minus underscore"); } $this->attributes['filename'] = $value; } /** * @return bool */ public function fileExist(): bool { return is_file($this->getPath()); } /** * @param $content * @throws BindingResolutionException */ public function setContentAttribute($content): void { $this->_content = app('image')->make($content)->orientate(); } /** * @return string|null */ public function getHashAttribute(): ?string { if (empty($this->content)) { return null; } if (empty($this->content->getEncoded())) { $this->content->encode(); } return sha1($this->content->getEncoded()); } /** * @return string|null */ public function getExtensionAttribute(): ?string { $mime = $this->content->mime(); if (empty($mime)) { return null; } return str_replace('image/', '', $mime); } /** * @return string|null */ public function getMimeAttribute(): ?string { if (empty($this->content)) { return null; } return $this->content->mime(); } /** * @param Image $image * @return bool */ public function same(Image $image): bool { $hash1 = $this->hash; $hash2 = $image->hash; return !empty($hash1) && !empty($hash2) && $hash1 === $hash2; } /** * @return InterventionImage|null * @throws BindingResolutionException */ public function getContentAttribute(): ?InterventionImage { if (empty($this->_content)) { try { $this->_content = app('image')->make($this->getPath()); } catch (NotReadableException $e) { return null; } } return $this->_content; } }