Completed
Push — master ( 4eda87...05e8ff )
by Mike
09:01 queued 05:03
created

Translatable   A

Complexity

Total Complexity 15

Size/Duplication

Total Lines 136
Duplicated Lines 0 %

Importance

Changes 3
Bugs 2 Features 0
Metric Value
wmc 15
eloc 57
c 3
b 2
f 0
dl 0
loc 136
rs 10

9 Methods

Rating   Name   Duplication   Size   Complexity  
A subSelectTranslation() 0 10 1
A locale() 0 5 1
A translations() 0 3 1
A scopeTranslated() 0 21 4
A bootTranslatable() 0 18 3
A saveTranslation() 0 6 1
A fullTextWildcards() 0 5 2
A getTranslatedFieldsForCurrentModel() 0 9 1
A scopeSearchFullText() 0 13 1
1
<?php
2
3
namespace Mikehins\Translatable;
4
5
use Illuminate\Support\Collection;
6
use Illuminate\Support\Facades\DB;
7
8
trait Translatable
9
{
10
	public static $transtableFieldName = 'translatable';
11
	
12
	public $translatable;
13
	public $model;
14
	
15
	protected $locale;
16
	
17
	public static function bootTranslatable()
18
	{
19
		static::addGlobalScope(new TranslatableScope);
20
		
21
		static::saving(function ($model) {
22
			$model->translatable = collect($model->attributes)->only(static::$transtableFieldName)->toArray();
23
			$model->attributes = collect($model->attributes)->except(static::$transtableFieldName)->toArray();
24
		});
25
		
26
		static::saved(function ($model) {
27
			if ($model->translatable) {
28
				(new self)->saveTranslation($model);
29
			}
30
		});
31
		
32
		static::deleted(function ($model) {
33
			if ((new $model)->has('translations')) {
34
				$model->translations()->delete();
35
			}
36
		});
37
	}
38
	
39
	public function locale($value = null)
40
	{
41
		$this->locale = $value;
42
		
43
		return $this;
44
	}
45
	
46
	// Relationship
47
	public function translations()
48
	{
49
		return $this->morphMany(Translation::class, 'translatable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

49
		return $this->/** @scrutinizer ignore-call */ morphMany(Translation::class, 'translatable');
Loading history...
50
	}
51
	
52
	// scope
53
	public function scopeTranslated($query, ...$fields)
54
	{
55
		$this->locale = $this->locale ?? app()->getLocale();
0 ignored issues
show
introduced by
The method getLocale() does not exist on Illuminate\Container\Container. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

55
		$this->locale = $this->locale ?? app()->/** @scrutinizer ignore-call */ getLocale();
Loading history...
56
		
57
		$fields = empty($fields) ? $this->getTranslatedFieldsForCurrentModel() : collect($fields);
58
		
59
		if ($fields->isEmpty()) {
60
			return $query;
61
		}
62
		
63
		// Add select * if nothing has been selected yet
64
		if ($query->getQuery()->columns === null) {
65
			$query->select('*');
66
		}
67
		
68
		// Build the sub select
69
		$fields->each(function ($key) use ($query) {
70
			$this->subSelectTranslation($query, $key);
71
		});
72
		
73
		return $query;
74
	}
75
	
76
	protected function subSelectTranslation($query, $key): void
77
	{
78
		$query->addSelect([
79
			$key => function ($query) use ($key) {
80
				$query->select(Translation::getTableName() . '.value')
81
					->from(Translation::getTableName())
82
					->where(Translation::getTableName() . '.translatable_type', '=', \get_class($this))
83
					->where(Translation::getTableName() . '.locale', '=', $this->locale)
84
					->where(Translation::getTableName() . '.key', '=', $key)
85
					->where(Translation::getTableName() . '.translatable_id', '=', \DB::raw($this->getTable() . '.' . $this->primaryKey));
0 ignored issues
show
Bug introduced by
It seems like getTable() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

85
					->where(Translation::getTableName() . '.translatable_id', '=', \DB::raw($this->/** @scrutinizer ignore-call */ getTable() . '.' . $this->primaryKey));
Loading history...
86
			}
87
		]);
88
89
//        $query->addSelect(\DB::raw('(
90
//			SELECT      `' . Translation::getTableName() . '`.`value`
91
//			  FROM      `' . Translation::getTableName() . '`
92
//			 WHERE      `' . Translation::getTableName() . '`.`translatable_type` = "' . \get_class($this) . '"
93
//			   AND      `' . Translation::getTableName() . '`.`locale` = "' . $this->locale . '"
94
//			   AND      `' . Translation::getTableName() . '`.`key` = "' . $key . '"
95
//			   AND      `' . Translation::getTableName() . '`.`translatable_id` = `' . $this->getTable() . '`.`' . $this->primaryKey . '`
96
//			) as `' . $key . '`')
97
//        );
98
	}
99
	
100
	/**
101
	 * Query the translations table for possible keys if none are provided.
102
	 * @return Collection
103
	 */
104
	public function getTranslatedFieldsForCurrentModel(): Collection
105
	{
106
		$table = Translation::getTableName();
107
		
108
		return DB::table($table)->select($table . '.key')
109
			->where($table . '.translatable_type', \get_class($this))
110
			->where($table . '.locale', $this->locale)
111
			->groupBy($table . '.key')
112
			->pluck('key');
113
	}
114
	
115
	public function saveTranslation($model): int
116
	{
117
		return (new Translation)->store([
118
			'translatable'      => $model->translatable[static::$transtableFieldName],
119
			'translatable_type' => \get_class($model),
120
			'translatable_id'   => $model->id,
121
		]);
122
	}
123
	
124
	protected function fullTextWildcards($term)
125
	{
126
		return collect(explode(' ', str_replace(['-', '+', '<', '>', '@', '(', ')', '~'], '', $term)))->map(function ($word, $key) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

126
		return collect(explode(' ', str_replace(['-', '+', '<', '>', '@', '(', ')', '~'], '', $term)))->map(function ($word, /** @scrutinizer ignore-unused */ $key) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
127
			return strlen($word) >= 3 ? '+' . $word . '*' : '';
128
		})->implode(' ');
129
	}
130
	
131
	public function scopeSearchFullText($query, $term)
132
	{
133
		return $query->join('translations', 'translations.translatable_id', '=', 'products.id')
134
			->addSelect([
135
				'relevance' => function ($query) use ($term) {
136
					$query->selectRaw("MATCH (`translations`.`value`) AGAINST ('" . $this->fullTextWildcards($term) . "' IN BOOLEAN MODE)")
137
						->from('translations')
138
						->where('translations.translatable_id', DB::raw($this->getTable() . '.' . $this->primaryKey))
139
						->limit(1);
140
				}
141
			])
142
			->whereRaw("MATCH (`translations`.`value`) AGAINST (? IN BOOLEAN MODE)", $this->fullTextWildcards($term))
143
			->orderByDesc('relevance');
144
	}
145
}
146