Initial commit
This commit is contained in:
252
app/Models/Tag.php
Normal file
252
app/Models/Tag.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Models\TaggableContext;
|
||||
use App\Models\TaggableLocale;
|
||||
use App\Traits\TaggableWithLocale;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Scout\Searchable;
|
||||
|
||||
class Tag extends \Cviebrock\EloquentTaggable\Models\Tag
|
||||
|
||||
/**
|
||||
* Class Tag extends \Cviebrock\EloquentTaggable\Models\Tag
|
||||
*
|
||||
* This extended the Tag class of cviebrock / eloquent-taggable package.
|
||||
* It adds an one-to-many relationship to store additional tag context data in the tag_contexts table.
|
||||
* Use this extension with the trait App\Traits\TaggableWithContext.
|
||||
*
|
||||
*/
|
||||
{
|
||||
use HasFactory;
|
||||
use TaggableWithLocale;
|
||||
use Searchable; // laravel/scout with ElasticSearch
|
||||
|
||||
protected $table = 'taggable_tags';
|
||||
protected $primaryKey = 'tag_id';
|
||||
protected $appends = ['locales', 'categories'];
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Boot the model and add event listeners.
|
||||
*
|
||||
* The 'deleting' event listener performs the following actions:
|
||||
* - Detaches all relationships with users, organizations, and banks.
|
||||
* - Deletes all locales and contexts directly tied to the tag.
|
||||
*
|
||||
* This listener ensures that when a Tag instance is deleted,
|
||||
* all related data and associations are properly cleaned up to
|
||||
* maintain data integrity and avoid orphaned records.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::deleting(function ($tag) {
|
||||
// Remove relationships of users, organizations, banks
|
||||
$tag->users()->detach();
|
||||
$tag->organizations()->detach();
|
||||
$tag->banks()->detach();
|
||||
// Delete locale tied directly to the tag
|
||||
$tag->locale()->delete();
|
||||
// Clean up any orphaned contexts (with no related tag models left)
|
||||
$unusedLocaleContexts = TaggableLocaleContext::doesntHave('tags')->get();
|
||||
$unusedLocaleContexts->each(fn ($localeContext) => $localeContext->delete());
|
||||
$unusedContexts = TaggableContext::doesntHave('tags')->get();
|
||||
$unusedContexts->each(fn ($context) => $context->delete());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the index name for the model.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function searchableAs()
|
||||
{
|
||||
return 'tags_index';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the value used to index the model.
|
||||
*/
|
||||
public function getScoutKey(): mixed
|
||||
{
|
||||
return $this->tag_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the key name used to index the model.
|
||||
*/
|
||||
public function getScoutKeyName(): mixed
|
||||
{
|
||||
return 'tag_id';
|
||||
}
|
||||
|
||||
|
||||
public function users()
|
||||
{
|
||||
return $this->morphedByMany(User::class, 'taggable', 'taggable_taggables', 'tag_id', 'taggable_id');
|
||||
}
|
||||
|
||||
|
||||
public function organizations()
|
||||
{
|
||||
return $this->morphedByMany(Organization::class, 'taggable', 'taggable_taggables', 'tag_id', 'taggable_id');
|
||||
}
|
||||
|
||||
|
||||
public function banks()
|
||||
{
|
||||
return $this->morphedByMany(Bank::class, 'taggable', 'taggable_taggables', 'tag_id', 'taggable_id');
|
||||
}
|
||||
|
||||
|
||||
public function locale()
|
||||
{
|
||||
return $this->hasOne(TaggableLocale::class, 'taggable_tag_id');
|
||||
}
|
||||
|
||||
|
||||
public function localeCode()
|
||||
{
|
||||
return $this->hasOne(TaggableLocale::class, 'taggable_tag_id')->select('locale');
|
||||
}
|
||||
|
||||
|
||||
public function contexts()
|
||||
{
|
||||
return $this->belongsToMany(TaggableContext::class, 'taggable_locale_context', 'tag_id', 'context_id');
|
||||
}
|
||||
|
||||
|
||||
public function localeContext()
|
||||
{
|
||||
return $this->hasOne(TaggableLocaleContext::class, 'tag_id');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scope to find tags by name.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param $value
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopeByLocalName(Builder $query, string $value): Builder
|
||||
{
|
||||
$normalized = app(TagService::class)->normalize($value);
|
||||
|
||||
return $query->where('normalized', $normalized);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get all related tags with their locales using a raw query.
|
||||
* Note: this is not a relationship definition!
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function translations()
|
||||
{
|
||||
return DB::table('taggable_tags as tt')
|
||||
->join('taggable_locale_context as tlc', 'tt.tag_id', '=', 'tlc.tag_id')
|
||||
->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
|
||||
->select('tt.*', 'tl.*', 'tlc.context_id')
|
||||
->whereIn('tlc.context_id', function ($query) {
|
||||
$query->select('context_id')
|
||||
->from('taggable_locale_context')
|
||||
->where('tag_id', $this->tag_id);
|
||||
})
|
||||
->distinct()
|
||||
->get();
|
||||
}
|
||||
|
||||
|
||||
public function getLocalesAttribute()
|
||||
{
|
||||
// Get context_ids for current tag
|
||||
$contextIds = DB::table('taggable_locale_context')
|
||||
->where('tag_id', $this->tag_id)
|
||||
->pluck('context_id');
|
||||
|
||||
// Get tag_ids that share these context_ids
|
||||
$relatedTagIds = DB::table('taggable_locale_context')
|
||||
->whereIn('context_id', $contextIds)
|
||||
->pluck('tag_id');
|
||||
|
||||
// Get translations for these tags
|
||||
return DB::table('taggable_tags as tt')
|
||||
->join('taggable_locales as tl', 'tt.tag_id', '=', 'tl.taggable_tag_id')
|
||||
->whereIn('tt.tag_id', $relatedTagIds)
|
||||
->select('tt.tag_id', 'tt.name', 'tl.*')
|
||||
->get();
|
||||
}
|
||||
|
||||
|
||||
public function getCategoriesAttribute()
|
||||
{
|
||||
// Get context_ids for current tag
|
||||
$contextIds = DB::table('taggable_locale_context')
|
||||
->where('tag_id', $this->tag_id)
|
||||
->pluck('context_id');
|
||||
|
||||
// Get categories for these contexts
|
||||
$categoryId = DB::table('taggable_contexts as tc')
|
||||
->join('categories as c', 'tc.category_id', '=', 'c.id')
|
||||
->whereIn('tc.id', $contextIds)
|
||||
->pluck('c.id');
|
||||
|
||||
// Get the first category ID
|
||||
$firstCategoryId = $categoryId->first();
|
||||
|
||||
// Find the category by its ID
|
||||
$category = Category::find($firstCategoryId);
|
||||
|
||||
$categoryWithRelated = $category ? $category->related() : null;
|
||||
|
||||
return $categoryWithRelated;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the translation attribute for the tag.
|
||||
*
|
||||
* This method attempts to retrieve the translation for the tag
|
||||
* in the current locale. If a translation in the current locale is
|
||||
* not found, it falls back to the base locale defined in the timebank-cc config.
|
||||
*
|
||||
* @return \App\Models\Translation|null The translation object for the category in the current or base locale, or null if not found.
|
||||
*/
|
||||
public function getTranslationAttribute()
|
||||
{
|
||||
$baseLocale = timebank_config('base_language');
|
||||
// Retrieve all translations using the translations() method
|
||||
$translations = $this->translations();
|
||||
|
||||
// Attempt to get the translation in the current locale
|
||||
$translation = $translations->firstWhere('locale', App::getLocale());
|
||||
|
||||
// Fallback to base locale if translation not found
|
||||
if (!$translation) {
|
||||
$translation = $translations->firstWhere('locale', $baseLocale);
|
||||
// Fallback to only available locale if no translation in base locale is not found
|
||||
if (!$translation) {
|
||||
return $translations->first(); //We can do first() as tags can only be translated to the base locale. So there are no other translations.
|
||||
}
|
||||
}
|
||||
return $translation;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user