![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/job-board.corals.io/vendor/cviebrock/eloquent-sluggable/src/Services/ |
<?php namespace Cviebrock\EloquentSluggable\Services; use Cocur\Slugify\Slugify; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; use Illuminate\Support\Collection; /** * Class SlugService * * @package Cviebrock\EloquentSluggable\Services */ class SlugService { /** * @var \Illuminate\Database\Eloquent\Model * @var \Cviebrock\EloquentSluggable\Sluggable */ protected $model; /** * Slug the current model. * * @param \Illuminate\Database\Eloquent\Model $model * @param bool $force * * @return bool */ public function slug(Model $model, bool $force = false): bool { $this->setModel($model); $attributes = []; foreach ($this->model->sluggable() as $attribute => $config) { if (is_numeric($attribute)) { $attribute = $config; $config = $this->getConfiguration(); } else { $config = $this->getConfiguration($config); } $slug = $this->buildSlug($attribute, $config, $force); if ($slug !== null) { $this->model->setAttribute($attribute, $slug); $attributes[] = $attribute; } } return $this->model->isDirty($attributes); } /** * Get the sluggable configuration for the current model, * including default values where not specified. * * @param array $overrides * * @return array */ public function getConfiguration(array $overrides = []): array { $defaultConfig = config('sluggable', []); return array_merge($defaultConfig, $overrides); } /** * Build the slug for the given attribute of the current model. * * @param string $attribute * @param array $config * @param bool $force * * @return null|string */ public function buildSlug(string $attribute, array $config, bool $force = null): ?string { $slug = $this->model->getAttribute($attribute); if ($force || $this->needsSlugging($attribute, $config)) { $source = $this->getSlugSource($config['source']); if ($source || is_numeric($source)) { $slug = $this->generateSlug($source, $config, $attribute); $slug = $this->validateSlug($slug, $config, $attribute); $slug = $this->makeSlugUnique($slug, $config, $attribute); } } return $slug; } /** * Determines whether the model needs slugging. * * @param string $attribute * @param array $config * * @return bool */ protected function needsSlugging(string $attribute, array $config): bool { $value = $this->model->getAttributeValue($attribute); if ( $config['onUpdate'] === true || $value === null || trim($value) === '' ) { return true; } if ($this->model->isDirty($attribute)) { return false; } return (!$this->model->exists); } /** * Get the source string for the slug. * * @param mixed $from * * @return string */ protected function getSlugSource($from): string { if (is_null($from)) { return $this->model->__toString(); } $sourceStrings = array_map(function($key) { $value = data_get($this->model, $key, $this->model->getAttribute($key)); if (is_bool($value)) { $value = (int) $value; } return $value; }, (array) $from); return implode(' ', $sourceStrings); } /** * Generate a slug from the given source string. * * @param string $source * @param array $config * @param string $attribute * * @return string * @throws \UnexpectedValueException */ protected function generateSlug(string $source, array $config, string $attribute): string { $separator = $config['separator']; $method = $config['method']; $maxLength = $config['maxLength']; $maxLengthKeepWords = $config['maxLengthKeepWords']; if ($method === null) { $slugEngine = $this->getSlugEngine($attribute, $config); $slug = $slugEngine->slugify($source, $separator); } elseif (is_callable($method)) { $slug = $method($source, $separator); } else { throw new \UnexpectedValueException('Sluggable "method" for ' . get_class($this->model) . ':' . $attribute . ' is not callable nor null.'); } $len = mb_strlen($slug); if (is_string($slug) && $maxLength && $len > $maxLength) { $reverseOffset = $maxLength - $len; $lastSeparatorPos = mb_strrpos($slug, $separator, $reverseOffset); if ($maxLengthKeepWords && $lastSeparatorPos !== false) { $slug = mb_substr($slug, 0, $lastSeparatorPos); } else { $slug = trim(mb_substr($slug, 0, $maxLength), $separator); } } return $slug; } /** * Return a class that has a `slugify()` method, used to convert * strings into slugs. * * @param string $attribute * * @param array $config * @return \Cocur\Slugify\Slugify */ protected function getSlugEngine(string $attribute, array $config): Slugify { static $slugEngines = []; $key = get_class($this->model) . '.' . $attribute; if (!array_key_exists($key, $slugEngines)) { $engine = new Slugify($config['slugEngineOptions']); $engine = $this->model->customizeSlugEngine($engine, $attribute); $slugEngines[$key] = $engine; } return $slugEngines[$key]; } /** * Checks that the given slug is not a reserved word. * * @param string $slug * @param array $config * @param string $attribute * * @return string * @throws \UnexpectedValueException */ protected function validateSlug(string $slug, array $config, string $attribute): string { $separator = $config['separator']; $reserved = $config['reserved']; if ($reserved === null) { return $slug; } // check for reserved names if ($reserved instanceof \Closure) { $reserved = $reserved($this->model); } if (is_array($reserved)) { if (in_array($slug, $reserved)) { $method = $config['uniqueSuffix']; $firstSuffix = $config['firstUniqueSuffix']; if ($method === null) { $suffix = $this->generateSuffix($slug, $separator, collect($reserved), $firstSuffix); } elseif (is_callable($method)) { $suffix = $method($slug, $separator, collect($reserved), $firstSuffix); } else { throw new \UnexpectedValueException('Sluggable "uniqueSuffix" for ' . get_class($this->model) . ':' . $attribute . ' is not null, or a closure.'); } return $slug . $separator . $suffix; } return $slug; } throw new \UnexpectedValueException('Sluggable "reserved" for ' . get_class($this->model) . ':' . $attribute . ' is not null, an array, or a closure that returns null/array.'); } /** * Checks if the slug should be unique, and makes it so if needed. * * @param string $slug * @param array $config * @param string $attribute * * @return string * @throws \UnexpectedValueException */ protected function makeSlugUnique(string $slug, array $config, string $attribute): string { if (!$config['unique']) { return $slug; } $separator = $config['separator']; // find all models where the slug is like the current one $list = $this->getExistingSlugs($slug, $attribute, $config); // if ... // a) the list is empty, or // b) our slug isn't in the list // ... we are okay if ( $list->count() === 0 || $list->contains($slug) === false ) { return $slug; } // if our slug is in the list, but // a) it's for our model, or // b) it looks like a suffixed version of our slug // ... we are also okay (use the current slug) if ($list->has($this->model->getKey())) { $currentSlug = $list->get($this->model->getKey()); if ( $currentSlug === $slug || !$slug || strpos($currentSlug, $slug) === 0 ) { return $currentSlug; } } $method = $config['uniqueSuffix']; $firstSuffix = $config['firstUniqueSuffix']; if ($method === null) { $suffix = $this->generateSuffix($slug, $separator, $list, $firstSuffix); } elseif (is_callable($method)) { $suffix = $method($slug, $separator, $list, $firstSuffix); } else { throw new \UnexpectedValueException('Sluggable "uniqueSuffix" for ' . get_class($this->model) . ':' . $attribute . ' is not null, or a closure.'); } return $slug . $separator . $suffix; } /** * Generate a unique suffix for the given slug (and list of existing, "similar" slugs. * * @param string $slug * @param string $separator * @param \Illuminate\Support\Collection $list * @param mixed $firstSuffix * * @return string */ protected function generateSuffix(string $slug, string $separator, Collection $list, $firstSuffix): string { $len = strlen($slug . $separator); // If the slug already exists, but belongs to // our model, return the current suffix. if ($list->search($slug) === $this->model->getKey()) { $suffix = explode($separator, $slug); return end($suffix); } $list->transform(function($value, $key) use ($len) { return (int) substr($value, $len); }); $max = $list->max(); // return one more than the largest value, // or return the first suffix the first time return (string) ($max === 0 ? $firstSuffix : $max + 1); } /** * Get all existing slugs that are similar to the given slug. * * @param string $slug * @param string $attribute * @param array $config * * @return \Illuminate\Support\Collection */ protected function getExistingSlugs(string $slug, string $attribute, array $config): Collection { $includeTrashed = $config['includeTrashed']; $query = $this->model->newQuery() ->findSimilarSlugs($attribute, $config, $slug); // use the model scope to find similar slugs $query->withUniqueSlugConstraints($this->model, $attribute, $config, $slug); // include trashed models if required if ($includeTrashed && $this->usesSoftDeleting()) { $query->withTrashed(); } // get the list of all matching slugs $results = $query->select([$attribute, $this->model->getQualifiedKeyName()]) ->get() ->toBase(); // key the results and return return $results->pluck($attribute, $this->model->getKeyName()); } /** * Does this model use softDeleting? * * @return bool */ protected function usesSoftDeleting(): bool { return method_exists($this->model, 'bootSoftDeletes'); } /** * Generate a unique slug for a given string. * * @param \Illuminate\Database\Eloquent\Model|string $model * @param string $attribute * @param string $fromString * @param array|null $config * * @return string * @throws \InvalidArgumentException * @throws \UnexpectedValueException */ public static function createSlug($model, string $attribute, string $fromString, array $config = null): string { if (is_string($model)) { $model = new $model; } /** @var static $instance */ $instance = (new static())->setModel($model); if ($config === null) { $config = Arr::get($model->sluggable(), $attribute); if ($config === null) { $modelClass = get_class($model); throw new \InvalidArgumentException("Argument 2 passed to SlugService::createSlug ['{$attribute}'] is not a valid slug attribute for model {$modelClass}."); } } elseif (!is_array($config)) { throw new \UnexpectedValueException('SlugService::createSlug expects an array or null as the fourth argument; ' . gettype($config) . ' given.'); } $config = $instance->getConfiguration($config); $slug = $instance->generateSlug($fromString, $config, $attribute); $slug = $instance->validateSlug($slug, $config, $attribute); $slug = $instance->makeSlugUnique($slug, $config, $attribute); return $slug; } /** * @param \Illuminate\Database\Eloquent\Model $model * * @return $this */ public function setModel(Model $model): self { $this->model = $model; return $this; } }