Commit 7d47697b authored by Nicolas Widart's avatar Nicolas Widart

Merge commit '64f060b5' as 'Modules/Tag'

parents 21f4cce8 64f060b5
.php_cs.cache
vendor/
composer.lock
Modules/
/.project
build/
<?php
$finder = Symfony\CS\Finder\DefaultFinder::create()
->exclude('Modules')
->exclude('vendor')
->in(__DIR__)
;
return Symfony\CS\Config\Config::create()
->setUsingCache(true)
->level(Symfony\CS\FixerInterface::PSR2_LEVEL)
->fixers(array(
// Concatenation should be used with at least one whitespace around.
'concat_with_spaces',
// Unused use statements must be removed.
'ordered_use',
// Removes extra empty lines.
'extra_empty_lines',
// Removes line breaks between use statements.
'remove_lines_between_uses',
// An empty line feed should precede a return statement.
'return',
// Unused use statements must be removed.
'unused_use',
// Remove trailing whitespace at the end of blank lines.
'whitespacy_lines',
// There MUST be one blank line after the namespace declaration.
'line_after_namespace',
// There should be exactly one blank line before a namespace declaration.
'single_blank_line_before_namespace',
// Each namespace use MUST go on its own line and there MUST be one blank line after the use statements block.
'single_line_after_imports',
// Ensure there is no code on the same line as the PHP open tag and it is followed by a blankline.
'blankline_after_open_tag',
// Remove duplicated semicolons.
'duplicate_semicolon',
// PHP multi-line arrays should have a trailing comma.
'multiline_array_trailing_comma',
// There should be no empty lines after class opening brace.
'no_blank_lines_after_class_opening',
// There should not be blank lines between docblock and the documented element.
'no_empty_lines_after_phpdocs',
// Phpdocs should start and end with content, excluding the very first and last line of the docblocks.
'phpdoc_trim',
// Removes line breaks between use statements.
'remove_lines_between_uses',
))
->finder($finder);
checks:
php: true
filter:
excluded_paths: [vendor/*, Tests/*, Modules/*]
coding_style:
php: { }
tools:
external_code_coverage: true
php_code_coverage: true
rules:
php.interface_has_no_interface_suffix:
enabled: false
language: php
php:
- 7
- 5.6
env:
- LARAVEL_VERSION="~5.2" TESTBENCH_VERSION="~3.2"
script:
- phpunit --coverage-text --coverage-clover=coverage.clover
before_script:
- travis_retry composer self-update
- travis_retry composer install --no-interaction --prefer-source
after_script:
- wget https://scrutinizer-ci.com/ocular.phar
- php ocular.phar code-coverage:upload --format=php-clover coverage.clover
sudo: false
notifications:
email:
- n.widart@gmail.com
<?php
namespace Modules\Tag\Blade\Facades;
use Illuminate\Support\Facades\Facade;
class TagWidget extends Facade
{
protected static function getFacadeAccessor()
{
return 'tag.widget.directive';
}
}
<?php
namespace Modules\Tag\Blade;
use Modules\Tag\Contracts\TaggableInterface;
use Modules\Tag\Repositories\TagRepository;
class TagWidget
{
/**
* @var TagRepository
*/
private $tag;
/**
* @var string
*/
private $namespace;
/**
* @var TaggableInterface|null
*/
private $entity;
/**
* @var string|null
*/
private $view;
/**
* @var string|null
*/
private $name;
public function __construct(TagRepository $tag)
{
$this->tag = $tag;
}
/**
* @param $arguments
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function show($arguments)
{
$this->extractArguments($arguments);
$view = $this->view ?: 'tag::admin.fields.tags';
$name = $this->name ?: 'Tags';
$availableTags = $this->tag->allForNamespace($this->namespace);
$tags = $this->getTags();
return view($view, compact('availableTags', 'tags', 'name'));
}
/**
* Extract the possible arguments as class properties
* @param array $arguments
*/
private function extractArguments(array $arguments)
{
$this->namespace = array_get($arguments, 0);
$this->entity = array_get($arguments, 1);
$this->view = array_get($arguments, 2);
$this->name = array_get($arguments, 3);
}
/**
* Get the available tags, if an entity is available from that
* @return array
*/
private function getTags()
{
if ($this->entity === null) {
return request()->old('tags', []);
}
return request()->old('tags', $this->entity->tags->pluck('slug')->toArray());
}
}
<?php
return [
'name' => 'Tag',
/*
|--------------------------------------------------------------------------
| Custom Sidebar Class
|--------------------------------------------------------------------------
| If you want to customise the admin sidebar ordering or grouping
| You can define your own sidebar class for this module.
| No custom sidebar: null
*/
'custom-sidebar' => null,
];
<?php
return [
'tag.tags' => [
'index' => 'tag::tags.list resource',
'create' => 'tag::tags.create resource',
'edit' => 'tag::tags.edit resource',
'destroy' => 'tag::tags.destroy resource',
],
];
<?php
namespace Modules\Tag\Contracts;
use Illuminate\Database\Eloquent\Builder;
interface TaggableInterface
{
/**
* The Eloquent tag entity name.
* @var string
*/
public static function getEntityNamespace();
/**
* Returns the Eloquent tags entity name.
* @return string
*/
public static function getTagsModel();
/**
* Sets the Eloquent tags entity name.
* @param string $model
* @return void
*/
public static function setTagsModel($model);
/**
* Get all the entities with the given tag(s)
* Optionally specify the column on which
* to perform the search operation.
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|array $tags
* @param string $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWhereTag(Builder $query, $tags, $type = 'slug');
/**
* Get all the entities with one of the given tag(s)
* Optionally specify the column on which
* to perform the search operation.
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string|array $tags
* @param string $type
* @return \Illuminate\Database\Eloquent\Builder
*/
public function scopeWithTag(Builder $query, $tags, $type = 'slug');
/**
* Define the eloquent morphMany relationship
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
public function tags();
/**
* Returns all the tags under the current entity namespace.
* @return \Illuminate\Database\Eloquent\Builder
*/
public static function allTags();
/**
* Creates a new model instance.
* @return \Illuminate\Database\Eloquent\Model
*/
public static function createTagsModel();
/**
* Syncs the given tags.
* @param string|array $tags
* @param string $type
* @return bool
*/
public function setTags($tags, $type = 'name');
/**
* Detaches multiple tags from the entity or if no tags are
* passed, removes all the attached tags from the entity.
* @param string|array|null $tags
* @return bool
*/
public function untag($tags = null);
/**
* Detaches the given tag from the entity.
* @param string $name
* @return void
*/
public function removeTag($name);
/**
* Attaches multiple tags to the entity.
* @param string|array $tags
* @return bool
*/
public function tag($tags);
/**
* Attaches the given tag to the entity.
* @param string $name
* @return void
*/
public function addTag($name);
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagTagsTable extends Migration
{
/**
* Run the migrations.
* @return void
*/
public function up()
{
Schema::create('tag__tags', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('namespace');
$table->timestamps();
});
Schema::create('tag__tagged', function (Blueprint $table) {
$table->increments('id');
$table->string('taggable_type');
$table->integer('taggable_id')->unsigned();
$table->integer('tag_id')->unsigned();
$table->index(['taggable_type', 'taggable_id']);
});
}
/**
* Reverse the migrations.
* @return void
*/
public function down()
{
Schema::drop('tag__tags');
Schema::drop('tag__tagged');
}
}
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTagTagTranslationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('tag__tag_translations', function (Blueprint $table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('slug');
$table->string('name');
$table->integer('tag_id')->unsigned();
$table->string('locale')->index();
$table->unique(['tag_id', 'locale']);
$table->foreign('tag_id')->references('id')->on('tag__tags')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::drop('tag__tag_translations');
}
}
<?php
namespace Modules\Tag\Database\Seeders;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Seeder;
class TagDatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
}
}
<?php
namespace Modules\Tag\Entities;
use Dimsav\Translatable\Translatable;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
use Translatable;
protected $table = 'tag__tags';
public $translatedAttributes = ['slug', 'name'];
protected $fillable = ['namespace', 'slug', 'name'];
}
<?php
namespace Modules\Tag\Entities;
use Illuminate\Database\Eloquent\Model;
class TagTranslation extends Model
{
public $timestamps = false;
protected $fillable = ['slug', 'name'];
protected $table = 'tag__tag_translations';
}
<?php
namespace Modules\Tag\Http\Controllers\Admin;
use Modules\Core\Http\Controllers\Admin\AdminBaseController;
use Modules\Tag\Entities\Tag;
use Modules\Tag\Http\Requests\CreateTagRequest;
use Modules\Tag\Http\Requests\UpdateTagRequest;
use Modules\Tag\Repositories\TagManager;
use Modules\Tag\Repositories\TagRepository;
class TagController extends AdminBaseController
{
/**
* @var TagRepository
*/
private $tag;
/**
* @var TagManager
*/
private $tagManager;
public function __construct(TagRepository $tag, TagManager $tagManager)
{
parent::__construct();
$this->tag = $tag;
$this->tagManager = $tagManager;
}
/**
* Display a listing of the resource.
*
* @return Response
*/
public function index()
{
$tags = $this->tag->all();
return view('tag::admin.tags.index', compact('tags'));
}
/**
* Show the form for creating a new resource.
*
* @return Response
*/
public function create()
{
$namespaces = $this->formatNamespaces($this->tagManager->getNamespaces());
return view('tag::admin.tags.create', compact('namespaces'));
}
/**
* Store a newly created resource in storage.
*
* @param CreateTagRequest $request
* @return Response
*/
public function store(CreateTagRequest $request)
{
$this->tag->create($request->all());
return redirect()->route('admin.tag.tag.index')
->withSuccess(trans('core::core.messages.resource created', ['name' => trans('tag::tags.tags')]));
}
/**
* Show the form for editing the specified resource.
*
* @param Tag $tag
* @return Response
*/
public function edit(Tag $tag)
{
$namespaces = $this->formatNamespaces($this->tagManager->getNamespaces());
return view('tag::admin.tags.edit', compact('tag', 'namespaces'));
}
/**
* Update the specified resource in storage.
*
* @param Tag $tag
* @param UpdateTagRequest $request
* @return Response
*/
public function update(Tag $tag, UpdateTagRequest $request)
{
$this->tag->update($tag, $request->all());
return redirect()->route('admin.tag.tag.index')
->withSuccess(trans('core::core.messages.resource updated', ['name' => trans('tag::tags.tags')]));
}
/**
* Remove the specified resource from storage.
*
* @param Tag $tag
* @return Response
*/
public function destroy(Tag $tag)
{
$this->tag->destroy($tag);
return redirect()->route('admin.tag.tag.index')
->withSuccess(trans('core::core.messages.resource deleted', ['name' => trans('tag::tags.tags')]));
}
private function formatNamespaces(array $namespaces)
{
$new = [];
foreach ($namespaces as $namespace) {
$new[$namespace] = $namespace;
}
return $new;
}
}
<?php
namespace Modules\Tag\Http\Requests;
use Modules\Core\Internationalisation\BaseFormRequest;
class CreateTagRequest extends BaseFormRequest
{
public function translationRules()
{
return [
'slug' => 'required',
'name' => 'required',
];
}
public function rules()
{
return [
'namespace' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
namespace Modules\Tag\Http\Requests;
use Modules\Core\Internationalisation\BaseFormRequest;
class UpdateTagRequest extends BaseFormRequest
{
public function translationRules()
{
return [
'slug' => 'required',
'name' => 'required',
];
}
public function rules()
{
return [
'namespace' => 'required',
];
}
public function authorize()
{
return true;
}
public function messages()
{
return [];
}
}
<?php
use Illuminate\Routing\Router;
/** @var Router $router */
$router->group(['prefix' => 'tag'], function (Router $router) {
$router->bind('tag__tag', function ($id) {
return app(\Modules\Tag\Repositories\TagRepository::class)->find($id);
});
$router->get('tags', [
'as' => 'admin.tag.tag.index',
'uses' => 'TagController@index',
'middleware' => 'can:tag.tags.index',
]);
$router->get('tags/create', [
'as' => 'admin.tag.tag.create',
'uses' => 'TagController@create',
'middleware' => 'can:tag.tags.create',
]);
$router->post('tags', [
'as' => 'admin.tag.tag.store',
'uses' => 'TagController@store',
'middleware' => 'can:tag.tags.create',
]);
$router->get('tags/{tag__tag}/edit', [
'as' => 'admin.tag.tag.edit',
'uses' => 'TagController@edit',
'middleware' => 'can:tag.tags.edit',
]);
$router->put('tags/{tag__tag}', [
'as' => 'admin.tag.tag.update',
'uses' => 'TagController@update',
'middleware' => 'can:tag.tags.edit',
]);
$router->delete('tags/{tag__tag}', [
'as' => 'admin.tag.tag.destroy',
'uses' => 'TagController@destroy',
'middleware' => 'can:tag.tags.destroy',
]);
});
# License (MIT)
Copyright (c) 2016 Nicolas Widart , n.widart@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
<?php
namespace Modules\Tag\Providers;
use Modules\Core\Providers\RoutingServiceProvider as CoreRoutingServiceProvider;
class RouteServiceProvider extends CoreRoutingServiceProvider
{
/**
* The root namespace to assume when generating URLs to actions.
* @var string
*/
protected $namespace = 'Modules\Tag\Http\Controllers';
/**
* @return string
*/
protected function getFrontendRoute()
{
return false;
}
/**
* @return string
*/
protected function getBackendRoute()
{
return __DIR__ . '/../Http/backendRoutes.php';
}
/**
* @return string
*/
protected function getApiRoute()
{
return false;
}
}
<?php
namespace Modules\Tag\Providers;
use Illuminate\Support\ServiceProvider;
use Modules\Core\Traits\CanPublishConfiguration;
use Modules\Tag\Blade\TagWidget;
use Modules\Tag\Entities\Tag;
use Modules\Tag\Repositories\Cache\CacheTagDecorator;
use Modules\Tag\Repositories\Eloquent\EloquentTagRepository;
use Modules\Tag\Repositories\TagManager;
use Modules\Tag\Repositories\TagManagerRepository;
use Modules\Tag\Repositories\TagRepository;
class TagServiceProvider extends ServiceProvider
{
use CanPublishConfiguration;
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = false;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->registerBindings();
$this->app->singleton('tag.widget.directive', function ($app) {
return new TagWidget($app[TagRepository::class]);
});
}
public function boot()
{
$this->publishConfig('tag', 'permissions');
$this->publishConfig('tag', 'config');
$this->registerBladeTags();
$this->loadMigrationsFrom(__DIR__.'/../Database/Migrations');
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array();
}
private function registerBindings()
{
$this->app->bind(TagRepository::class, function () {
$repository = new EloquentTagRepository(new Tag());
if (! config('app.cache')) {
return $repository;
}
return new CacheTagDecorator($repository);
});
$this->app->singleton(TagManager::class, function () {
return new TagManagerRepository();
});
}
protected function registerBladeTags()
{
if (app()->environment() === 'testing') {
return;
}
$this->app['blade.compiler']->directive('tags', function ($value) {
return "<?php echo TagWidget::show([$value]); ?>";
});
}
}
<?php
namespace Modules\Tag\Repositories\Cache;
use Modules\Core\Repositories\Cache\BaseCacheDecorator;
use Modules\Tag\Repositories\TagRepository;
class CacheTagDecorator extends BaseCacheDecorator implements TagRepository
{
public function __construct(TagRepository $tag)
{
parent::__construct();
$this->entityName = 'tag.tags';
$this->repository = $tag;
}
/**
* Get all the tags in the given namespace
* @param string $namespace
* @return \Illuminate\Database\Eloquent\Collection
*/
public function allForNamespace($namespace)
{
return $this->cache
->tags([$this->entityName, 'global'])
->remember("{$this->locale}.{$this->entityName}.allForNamespace.{$namespace}", $this->cacheTime,
function () use ($namespace) {
return $this->repository->allForNamespace($namespace);
}
);
}
}
<?php
namespace Modules\Tag\Repositories\Eloquent;
use Modules\Core\Repositories\Eloquent\EloquentBaseRepository;
use Modules\Tag\Repositories\TagRepository;
class EloquentTagRepository extends EloquentBaseRepository implements TagRepository
{
/**
* Get all the tags in the given namespace
* @param string $namespace
* @return \Illuminate\Database\Eloquent\Collection
*/
public function allForNamespace($namespace)
{
return $this->model->with('translations')->where('namespace', $namespace)->get();
}
}
<?php
namespace Modules\Tag\Repositories;
use Modules\Tag\Contracts\TaggableInterface;
interface TagManager
{
/**
* Returns all the registered namespaces.
* @return array
*/
public function getNamespaces();
/**
* Registers an entity namespace.
* @param TaggableInterface $entity
* @return void
*/
public function registerNamespace(TaggableInterface $entity);
}
<?php
namespace Modules\Tag\Repositories;
use Modules\Tag\Contracts\TaggableInterface;
class TagManagerRepository implements TagManager
{
/**
* Array of registered namespaces.
* @var array
*/
private $namespaces = [];
/**
* Returns all the registered namespaces.
* @return array
*/
public function getNamespaces()
{
return $this->namespaces;
}
/**
* Registers an entity namespace.
* @param TaggableInterface $entity
* @return void
*/
public function registerNamespace(TaggableInterface $entity)
{
$this->namespaces[] = $entity->getEntityNamespace();
}
}
<?php
namespace Modules\Tag\Repositories;
use Modules\Core\Repositories\BaseRepository;
interface TagRepository extends BaseRepository
{
/**
* Get all the tags in the given namespace
* @param string $namespace
* @return \Illuminate\Database\Eloquent\Collection
*/
public function allForNamespace($namespace);
}
<?php
return [
'list resource' => 'List tags',
'create resource' => 'Create tags',
'edit resource' => 'Edit tags',
'destroy resource' => 'Destroy tags',
'tags' => 'Tags',
'tag' => 'Tag',
'create tag' => 'Create a tag',
'edit tag' => 'Edit a tag',
'name' => 'Name',
'slug' => 'Slug',
'namespace' => 'Namespace',
];
<div class='form-group{{ $errors->has('tags') ? ' has-error' : '' }}'>
{!! Form::label('tags', $name) !!}
<select name="tags[]" id="tags" class="input-tags" multiple>
<?php foreach ($availableTags as $tag): ?>
<option value="{{ $tag->slug }}" {{ in_array($tag->slug, $tags) ? ' selected' : null }}>{{ $tag->name }}</option>
<?php endforeach; ?>
</select>
{!! $errors->first('tags', '<span class="help-block">:message</span>') !!}
</div>
<script>
$( document ).ready(function() {
$('.input-tags').selectize({
plugins: ['remove_button'],
delimiter: ',',
persist: false,
create: function(input) {
return {
value: input,
text: input
}
}
});
});
</script>
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('tag::tags.create tag') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.tag.tag.index') }}">{{ trans('tag::tags.tags') }}</a></li>
<li class="active">{{ trans('tag::tags.create tag') }}</li>
</ol>
@stop
@section('styles')
@stop
@section('content')
{!! Form::open(['route' => ['admin.tag.tag.store'], 'method' => 'post']) !!}
<div class="row">
<div class="col-md-9">
<div class="nav-tabs-custom">
@include('partials.form-tab-headers')
<div class="tab-content">
<?php $i = 0; ?>
@foreach (LaravelLocalization::getSupportedLocales() as $locale => $language)
<?php $i++; ?>
<div class="tab-pane {{ locale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('tag::admin.tags.partials.create-fields', ['lang' => $locale])
</div>
@endforeach
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.create') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ route('admin.tag.tag.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div> {{-- end nav-tabs-custom --}}
</div>
<div class="col-md-3">
<div class="box box-primary">
<div class="box-body">
<div class="form-group {{ $errors->has('namespace') ? 'has-error' : '' }}">
{!! Form::label('namespace', trans('tag::tags.namespace')) !!}
{!! Form::select('namespace', $namespaces, old('namespace') , ['class' => 'selectize']) !!}
{!! $errors->first('namespace', '<span class="help-block">:message</span>') !!}
</div>
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$('.selectize').selectize();
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.tag.tag.index') ?>" }
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('tag::tags.edit tag') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li><a href="{{ route('admin.tag.tag.index') }}">{{ trans('tag::tags.tags') }}</a></li>
<li class="active">{{ trans('tag::tags.edit tag') }}</li>
</ol>
@stop
@section('styles')
{!! Theme::script('js/vendor/ckeditor/ckeditor.js') !!}
@stop
@section('content')
{!! Form::open(['route' => ['admin.tag.tag.update', $tag->id], 'method' => 'put']) !!}
<div class="row">
<div class="col-md-9">
<div class="nav-tabs-custom">
@include('partials.form-tab-headers')
<div class="tab-content">
<?php $i = 0; ?>
@foreach (LaravelLocalization::getSupportedLocales() as $locale => $language)
<?php $i++; ?>
<div class="tab-pane {{ locale() == $locale ? 'active' : '' }}" id="tab_{{ $i }}">
@include('tag::admin.tags.partials.edit-fields', ['lang' => $locale])
</div>
@endforeach
<div class="box-footer">
<button type="submit" class="btn btn-primary btn-flat">{{ trans('core::core.button.update') }}</button>
<button class="btn btn-default btn-flat" name="button" type="reset">{{ trans('core::core.button.reset') }}</button>
<a class="btn btn-danger pull-right btn-flat" href="{{ route('admin.tag.tag.index')}}"><i class="fa fa-times"></i> {{ trans('core::core.button.cancel') }}</a>
</div>
</div>
</div> {{-- end nav-tabs-custom --}}
</div>
<div class="col-md-3">
<div class="box box-primary">
<div class="box-body">
<div class="form-group {{ $errors->has('namespace') ? 'has-error' : '' }}">
{!! Form::label('namespace', trans('tag::tags.namespace')) !!}
{!! Form::select('namespace', $namespaces, old('namespace', $tag->namespace) , ['class' => 'selectize']) !!}
{!! $errors->first('namespace', '<span class="help-block">:message</span>') !!}
</div>
</div>
</div>
</div>
</div>
{!! Form::close() !!}
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>b</code></dt>
<dd>{{ trans('core::core.back to index') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$('.selectize').selectize();
$(document).keypressAction({
actions: [
{ key: 'b', route: "<?= route('admin.tag.tag.index') ?>" }
]
});
});
</script>
<script>
$( document ).ready(function() {
$('input[type="checkbox"].flat-blue, input[type="radio"].flat-blue').iCheck({
checkboxClass: 'icheckbox_flat-blue',
radioClass: 'iradio_flat-blue'
});
});
</script>
@stop
@extends('layouts.master')
@section('content-header')
<h1>
{{ trans('tag::tags.tags') }}
</h1>
<ol class="breadcrumb">
<li><a href="{{ route('dashboard.index') }}"><i class="fa fa-dashboard"></i> {{ trans('core::core.breadcrumb.home') }}</a></li>
<li class="active">{{ trans('tag::tags.tags') }}</li>
</ol>
@stop
@section('content')
<div class="row">
<div class="col-xs-12">
<div class="row">
<div class="btn-group pull-right" style="margin: 0 15px 15px 0;">
<a href="{{ route('admin.tag.tag.create') }}" class="btn btn-primary btn-flat" style="padding: 4px 10px;">
<i class="fa fa-pencil"></i> {{ trans('tag::tags.create tag') }}
</a>
</div>
</div>
<div class="box box-primary">
<div class="box-header">
</div>
<!-- /.box-header -->
<div class="box-body">
<table class="data-table table table-bordered table-hover">
<thead>
<tr>
<th>{{ trans('tag::tags.name') }}</th>
<th>{{ trans('tag::tags.slug') }}</th>
<th>{{ trans('tag::tags.namespace') }}</th>
<th data-sortable="false">{{ trans('core::core.table.actions') }}</th>
</tr>
</thead>
<tbody>
<?php if (isset($tags)): ?>
<?php foreach ($tags as $tag): ?>
<tr>
<td>
<a href="{{ route('admin.tag.tag.edit', [$tag->id]) }}">
{{ $tag->name }}
</a>
</td>
<td>
<a href="{{ route('admin.tag.tag.edit', [$tag->id]) }}">
{{ $tag->slug }}
</a>
</td>
<td>
<a href="{{ route('admin.tag.tag.edit', [$tag->id]) }}">
{{ $tag->namespace }}
</a>
</td>
<td>
<div class="btn-group">
<a href="{{ route('admin.tag.tag.edit', [$tag->id]) }}" class="btn btn-default btn-flat"><i class="fa fa-pencil"></i></a>
<button class="btn btn-danger btn-flat" data-toggle="modal" data-target="#modal-delete-confirmation" data-action-target="{{ route('admin.tag.tag.destroy', [$tag->id]) }}"><i class="fa fa-trash"></i></button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
<!-- /.box-body -->
</div>
<!-- /.box -->
</div>
</div>
</div>
@include('core::partials.delete-modal')
@stop
@section('footer')
<a data-toggle="modal" data-target="#keyboardShortcutsModal"><i class="fa fa-keyboard-o"></i></a> &nbsp;
@stop
@section('shortcuts')
<dl class="dl-horizontal">
<dt><code>c</code></dt>
<dd>{{ trans('tag::tags.title.create tag') }}</dd>
</dl>
@stop
@section('scripts')
<script type="text/javascript">
$( document ).ready(function() {
$(document).keypressAction({
actions: [
{ key: 'c', route: "<?= route('admin.tag.tag.create') ?>" }
]
});
});
</script>
<?php $locale = locale(); ?>
<script type="text/javascript">
$(function () {
$('.data-table').dataTable({
"paginate": true,
"lengthChange": true,
"filter": true,
"sort": true,
"info": true,
"autoWidth": true,
"order": [[ 0, "desc" ]],
"language": {
"url": '<?php echo Module::asset("core:js/vendor/datatables/{$locale}.json") ?>'
}
});
});
</script>
@stop
<div class="box-body">
{!! Form::i18nInput('name', trans('tag::tags.name'), $errors, $lang, null, ['data-slug' => 'source']) !!}
{!! Form::i18nInput('slug', trans('tag::tags.slug'), $errors, $lang, null, ['data-slug' => 'target']) !!}
</div>
<div class="box-body">
{!! Form::i18nInput('name', 'Name', $errors, $lang, $tag, ['data-slug' => 'source']) !!}
{!! Form::i18nInput('slug', 'Slug', $errors, $lang, $tag, ['data-slug' => 'target']) !!}
</div>
<?php
namespace Modules\Tag\Sidebar;
use Maatwebsite\Sidebar\Group;
use Maatwebsite\Sidebar\Item;
use Maatwebsite\Sidebar\Menu;
use Modules\User\Contracts\Authentication;
class SidebarExtender implements \Maatwebsite\Sidebar\SidebarExtender
{
/**
* @var Authentication
*/
protected $auth;
/**
* @param Authentication $auth
*
* @internal param Guard $guard
*/
public function __construct(Authentication $auth)
{
$this->auth = $auth;
}
/**
* @param Menu $menu
*
* @return Menu
*/
public function extendWith(Menu $menu)
{
$menu->group(trans('core::sidebar.content'), function (Group $group) {
$group->item(trans('tag::tags.tags'), function (Item $item) {
$item->icon('fa fa-tag');
$item->weight(0);
$item->route('admin.tag.tag.index');
$item->authorize(
$this->auth->hasAccess('tag.tags.index')
);
});
});
return $menu;
}
}
<?php
namespace Modules\Tag\Tests;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\View\ViewServiceProvider;
use Modules\Core\Providers\CoreServiceProvider;
use Modules\Page\Providers\PageServiceProvider;
use Modules\Tag\Providers\TagServiceProvider;
use Nwidart\Modules\LaravelModulesServiceProvider;
use Orchestra\Testbench\TestCase;
abstract class BaseTestCase extends TestCase
{
public function setUp()
{
parent::setUp();
$this->resetDatabase();
}
protected function getPackageProviders($app)
{
return [
ViewServiceProvider::class,
LaravelModulesServiceProvider::class,
CoreServiceProvider::class,
PageServiceProvider::class,
TagServiceProvider::class,
];
}
protected function getEnvironmentSetUp($app)
{
$app['path.base'] = __DIR__ . '/..';
$app['config']->set('database.default', 'sqlite');
$app['config']->set('database.connections.sqlite', array(
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
));
$app['config']->set('translatable.locales', ['en', 'fr']);
$app['config']->set('modules.paths.modules', __DIR__ . '/../Modules');
}
private function resetDatabase()
{
// Relative to the testbench app folder: vendors/orchestra/testbench/src/fixture
$migrationsPath = 'Database/Migrations';
$artisan = $this->app->make(Kernel::class);
// Makes sure the migrations table is created
$artisan->call('migrate', [
'--database' => 'sqlite',
]);
// We empty all tables
$artisan->call('migrate:reset', [
'--database' => 'sqlite',
]);
// Migrate
$artisan->call('migrate', [
'--database' => 'sqlite',
]);
$artisan->call('migrate', [
'--database' => 'sqlite',
'--path' => 'Modules/Page/Database/Migrations',
]);
}
}
<?php
namespace Modules\Tag\Tests\Integration;
use Modules\Tag\Repositories\TagRepository;
use Modules\Tag\Tests\BaseTestCase;
class TagRepositoryTest extends BaseTestCase
{
/**
* @var TagRepository
*/
private $tag;
public function setUp()
{
parent::setUp();
$this->tag = app(TagRepository::class);
}
/** @test */
public function it_gets_all_tags_for_a_namespace()
{
$this->tag->create([
'namespace' => 'asgardcms/media',
'en' => [
'slug' => 'media-tag',
'name' => 'media tag',
],
]);
$this->tag->create([
'namespace' => 'asgardcms/media',
'en' => [
'slug' => 'media-tag',
'name' => 'media tag',
],
]);
$this->tag->create([
'namespace' => 'asgardcms/blog',
'en' => [
'slug' => 'media-tag',
'name' => 'media tag',
],
]);
$this->assertCount(1, $this->tag->allForNamespace('asgardcms/blog'));
}
}
<?php
namespace Modules\Tag\Tests\Integration;
use Modules\Page\Entities\Page;
use Modules\Page\Repositories\PageRepository;
use Modules\Tag\Repositories\TagRepository;
use Modules\Tag\Tests\BaseTestCase;
class TaggableTraitTest extends BaseTestCase
{
/**
* @var TagRepository
*/
private $tag;
/**
* @var PageRepository
*/
private $page;
public function setUp()
{
parent::setUp();
$this->tag = app(TagRepository::class);
$this->page = app(PageRepository::class);
}
/** @test */
public function it_creates_tags_on_creation_of_related_model()
{
$page = $this->createPage();
$page->setTags(['my first tag']);
$this->assertCount(1, $this->tag->all());
}
/** @test */
public function it_can_create_multiple_tags_at_once()
{
$page = $this->createPage();
$page->setTags(['my first tag', 'second tag', 'third tag']);
$this->assertCount(3, $this->tag->all());
}
/** @test */
public function it_links_tags_to_entity()
{
$page = $this->createPage();
$page->setTags(['my first tag']);
$page = $this->page->findHomepage();
$this->assertCount(1, $page->tags);
}
/** @test */
public function it_removes_tags_from_link_with_entity()
{
$this->createPage(['original tag']);
$page = $this->page->findHomepage();
$page->setTags(['my first tag']);
$page = $this->page->findHomepage();
$this->assertCount(1, $page->tags);
}
/** @test */
public function it_can_get_pages_with_one_of_specified_tags()
{
$this->createPage(['original tag']);
$this->createPage(['original-tag']);
$this->createPage(['random tag']);
$this->assertCount(2, Page::withTag(['original-tag', 'some-other-tag'])->get());
}
/** @test */
public function it_gets_pages_with_all_specified_tags()
{
$this->createPage(['original tag']);
$this->createPage(['original-tag']);
$this->createPage(['random tag', 'original-tag']);
$this->assertCount(1, Page::whereTag(['original-tag', 'random-tag'])->get());
}
/** @test */
public function it_gets_all_tags_for_a_namespace()
{
$this->createPage(['original tag', 'other tag', 'random tag']);
$this->tag->create([
'namespace' => 'asgardcms/media',
'en' => [
'slug' => 'media-tag',
'name' => 'media tag',
],
]);
$this->assertCount(3, Page::allTags()->get());
}
private function createPage(array $tags = [])
{
return $this->page->create([
'is_home' => 1,
'template' => 'default',
'en' => [
'title' => 'My Page',
'slug' => 'my-page',
'body' => 'My Page Body',
],
'tags' => $tags,
]);
}
}
<?php
namespace Modules\Tag\Tests;
use Modules\Tag\Contracts\TaggableInterface;
use Modules\Tag\Repositories\TagManager;
use Modules\Tag\Traits\TaggableTrait;
class TagManagerRepositoryTest extends BaseTestCase
{
/**
* @var TagManager
*/
private $tagManager;
public function setUp()
{
parent::setUp();
$this->tagManager = app(TagManager::class);
}
/** @test */
public function it_initialises_empty_array()
{
$this->assertEquals(['asgardcms/page'], $this->tagManager->getNamespaces());
}
/** @test */
public function it_adds_items_to_array()
{
$this->tagManager->registerNamespace(new TestModel());
$this->assertCount(2, $this->tagManager->getNamespaces());
}
}
class TestModel implements TaggableInterface
{
use TaggableTrait, \Modules\Core\Traits\NamespacedEntity;
protected static $entityNamespace = 'asgardcms/media';
}
<?php
namespace Modules\Tag\Traits;
use Illuminate\Database\Eloquent\Builder;
use Modules\Tag\Entities\Tag;
trait TaggableTrait
{
/**
* {@inheritdoc}
*/
protected static $tagsModel = Tag::class;
/**
* {@inheritdoc}
*/
public static function getTagsModel()
{
return static::$tagsModel;
}
/**
* {@inheritdoc}
*/
public static function setTagsModel($model)
{
static::$tagsModel = $model;
}
/**
* {@inheritdoc}
*/
public function scopeWhereTag(Builder $query, $tags, $type = 'slug')
{
if (is_string($tags) === true) {
$tags = [$tags];
}
$query->with('translations');
foreach ($tags as $tag) {
$query->whereHas('tags', function (Builder $query) use ($type, $tag) {
$query->whereHas('translations', function (Builder $query) use ($type, $tag) {
$query->where($type, $tag);
});
});
}
return $query;
}
/**
* {@inheritdoc}
*/
public function scopeWithTag(Builder $query, $tags, $type = 'slug')
{
if (is_string($tags) === true) {
$tags = [$tags];
}
$query->with('translations');
return $query->whereHas('tags', function (Builder $query) use ($type, $tags) {
$query->whereHas('translations', function (Builder $query) use ($type, $tags) {
$query->whereIn($type, $tags);
});
});
}
/**
* {@inheritdoc}
*/
public function tags()
{
return $this->morphToMany(static::$tagsModel, 'taggable', 'tag__tagged', 'taggable_id', 'tag_id');
}
/**
* {@inheritdoc}
*/
public static function createTagsModel()
{
return new static::$tagsModel;
}
/**
* {@inheritdoc}
*/
public static function allTags()
{
$instance = new static;
return $instance->createTagsModel()->with('translations')->whereNamespace($instance->getEntityClassName());
}
/**
* {@inheritdoc}
*/
public function setTags($tags, $type = 'slug')
{
// Get the current entity tags
$entityTags = $this->tags->pluck($type)->all();
// Prepare the tags to be added and removed
$tagsToAdd = array_diff($tags, $entityTags);
$tagsToDel = array_diff($entityTags, $tags);
// Detach the tags
if (count($tagsToDel) > 0) {
$this->untag($tagsToDel);
}
// Attach the tags
if (count($tagsToAdd) > 0) {
$this->tag($tagsToAdd);
}
return true;
}
/**
* {@inheritdoc}
*/
public function tag($tags)
{
foreach ($tags as $tag) {
$this->addTag($tag);
}
return true;
}
/**
* {@inheritdoc}
*/
public function addTag($name)
{
$tag = $this->createTagsModel()->where('namespace', $this->getEntityClassName())
->with('translations')
->whereHas('translations', function (Builder $q) use ($name) {
$q->where('slug', $this->generateTagSlug($name));
})->first();
if ($tag === null) {
$tag = new Tag([
'namespace' => $this->getEntityClassName(),
locale() => [
'slug' => $this->generateTagSlug($name),
'name' => $name,
],
]);
}
if ($tag->exists === false) {
$tag->save();
}
if ($this->tags->contains($tag->id) === false) {
$this->tags()->attach($tag);
}
}
/**
* {@inheritdoc}
*/
public function untag($tags = null)
{
$tags = $tags ?: $this->tags->pluck('name')->all();
foreach ($tags as $tag) {
$this->removeTag($tag);
}
return true;
}
/**
* {@inheritdoc}
*/
public function removeTag($name)
{
$tag = $this->createTagsModel()
->where('namespace', $this->getEntityClassName())
->with('translations')
->whereHas('translations', function (Builder $q) use ($name) {
$q->orWhere('name', $this->generateTagSlug($name));
$q->orWhere('slug', $this->generateTagSlug($name));
})->first();
if ($tag) {
$this->tags()->detach($tag);
}
}
/**
* {@inheritdoc}
*/
protected function getEntityClassName()
{
if (isset(static::$entityNamespace)) {
return static::$entityNamespace;
}
return $this->tags()->getMorphClass();
}
/**
* {@inheritdoc}
*/
protected function generateTagSlug($name)
{
return str_slug($name);
}
}
url: https://github.com/AsgardCms/Tag
versions:
"1.0@unreleased":
added:
- Initial Release
{
"name": "asgardcms/tag-module",
"description": "Tag management in AsgardCMS",
"type": "asgard-module",
"license": "MIT",
"keywords": [
"asgardcms",
"tags",
"taggable"
],
"authors": [
{
"name": "Nicolas Widart",
"email": "n.widart@gmail.com"
}
],
"require": {
"php": ">=5.6",
"composer/installers": "~1.0",
"asgardcms/core-module": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0",
"orchestra/testbench": "3.3.*",
"asgardcms/page-module": "~2.0",
"phpro/grumphp": "^0.9.1",
"friendsofphp/php-cs-fixer": "^1.11"
},
"autoload": {
"psr-4": {
"Modules\\Tag\\": ""
}
},
"autoload-dev": {
"psr-4": {
"Modules\\Tag\\": ".",
"Modules\\": "Modules/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"minimum-stability": "dev",
"prefer-stable": true
}
parameters:
git_dir: .
bin_dir: vendor/bin
stop_on_failure: true
tasks:
phpcsfixer:
config_file: .php_cs
composer:
file: ./composer.json
jsonlint:
ignore_patterns: []
{
"name": "Tag",
"alias": "tag",
"description": "Tag management in AsgardCMS",
"keywords": [
"Tag",
"AsgardCMS",
"taggable"
],
"active": 1,
"order": 1,
"providers": [
"Modules\\Tag\\Providers\\TagServiceProvider",
"Modules\\Tag\\Providers\\RouteServiceProvider"
],
"aliases":{
"TagWidget": "Modules\\Tag\\Blade\\Facades\\TagWidget"
},
"files": []
}
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="Package Test Suite">
<directory suffix=".php">./Tests/</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">.</directory>
<exclude>
<directory suffix=".php">vendor/</directory>
<directory suffix=".php">Modules/</directory>
<directory suffix=".php">Repositories/Cache/</directory>
<directory suffix=".php">Resources/</directory>
<directory suffix=".php">Http/</directory>
<directory suffix=".php">Config/</directory>
<directory suffix=".php">Sidebar/</directory>
<directory suffix=".php">Providers/</directory>
<directory suffix=".php">Tests/</directory>
</exclude>
</whitelist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
<log type="coverage-html" target="build/coverage" charset="UTF-8" yui="true" highlight="true"/>
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
</phpunit>
# Tag Module
[![Latest Version](https://img.shields.io/github/release/asgardcms/tag.svg?style=flat-square)](https://github.com/asgardcms/tag/releases)
[![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)
[![Build Status](https://img.shields.io/travis/AsgardCms/Tag/master.svg?style=flat-square)](https://travis-ci.org/AsgardCms/Tag)
[![Scrutinizer Coverage](https://img.shields.io/scrutinizer/coverage/g/AsgardCms/Tag.svg?maxAge=86400&style=flat-square)](https://scrutinizer-ci.com/g/AsgardCms/Tag/?branch=master)
[![Quality block](https://img.shields.io/scrutinizer/g/asgardcms/tag.svg?style=flat-square)](https://scrutinizer-ci.com/g/asgardcms/tag)
[![SensioLabs Insight](https://img.shields.io/sensiolabs/i/0919e4aa-8e6c-43f0-860d-7626cddaf498.svg)](https://insight.sensiolabs.com/projects/0919e4aa-8e6c-43f0-860d-7626cddaf498)
[![Slack](http://slack.asgardcms.com/badge.svg)](http://slack.asgardcms.com/)
| Branch | Travis-ci |
| ---------------- | --------------- |
| master | [![Build Status](https://travis-ci.org/AsgardCms/Tag.svg?branch=master)](https://travis-ci.org/AsgardCms/Tag) |
An AsgardCMS module which enabled tagging of any entity with ease.
## Installation
### Composer
```
composer require asgardcms/tag-module
```
### Migrations
Run the migrations for the tag module
```
php artisan module:migrate tag
```
### Permissions
Go to the Admin role, and give yourself the permissions for the Tag Module.
## Usage
Any of you entities can have tags attached to it. To enable this your entity needs to implement an interface, use a trait and that's it.
### 1. Add interface & trait on desired entity
Your entity needs to implement the `Modules\Tag\Contracts\TaggableInterface` interface.
In order for your entity to satisfy this interface it needs to use the following traits:
- `Modules\Core\Traits\NamespacedEntity`
- `Modules\Tag\Traits\TaggableTrait`
Tags are organised by namespace. This is used in order to get the tags for a specific namespace on the display of the field. It also creates tags for that namespace if tags need to be created.
By default the `TaggableTrait` will use the full namespace of your entity. However, you can specify a nicer / shorter namespace to use by using the static `$entityNamespace` property on your entity.
Example:
``` php
protected static $entityNamespace = 'asgardcms/media';
```
### 2. Defining a new namespace to use for tags
In your module Service Provider, `boot()` method, you now need to add the namespace it's going to use. This can be done using the `TagManager` interface.
``` php
$this->app[TagManager::class]->registerNamespace(new File());
```
And with this, the Tag Module is aware of the new namespace.
### 3. Display the tag field on your views
By using a custom blade directive you can include the tags field on your views.
- The first argument is the namespace to get the tags for.
- (optional) Second argument is the entity to fetch the tags for (pre-filling the input if tags are present for given entity).
- (optional) Third and last argument can be a view to use. This will override the default tags view with its input field.
```` php
@tags('asgardcms/media', $file)
````
### 4. Store tags
In your repositories you need to call the `setTags()` method to persist the tags on your entity.
``` php
$file->setTags(array_get($data, 'tags'));
```
And that's all on how to use tags for your entities.
## Convenience methods
### Scope: `withTag()`
Get all the entities with one of the given tag(s). Optionally specify the column on which to perform the search operation, defaults to the `slug` column.
Example in your repository :
``` php
// Get all files with either of the 2 tags
$files = $this->file->withTag(['your-first-tag', 'some-other-tag'])->get();
```
### Scope: `whereTag()`
Get all the entities with the given tag(s). Optionally specify the column on which to perform the search operation, defaults to `slug` column.
Example in your repository :
``` php
// Get all files with all given tags
$files = $this->file->whereTag(['your-first-tag', 'some-other-tag'])->get();
// Get all files with the given tag
$files = $this->file->whereTag('your-first-tag')->get();
```
### `allTags()`: Get all the tags for the entity
You can fetch all the tags for an entity by using the `allTags()` method.
``` php
$tags = $file->allTags();
```
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment