Translatable::getTranslationBaseQuery()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 10
c 0
b 0
f 0
nc 1
cc 1
nop 1
1
<?php
2
3
namespace BrunoFernandes\LaravelMultiLanguage;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Facades\App;
7
use Illuminate\Database\Eloquent\Builder;
8
use BrunoFernandes\LaravelMultiLanguage\Scopes\LangScope;
9
use BrunoFernandes\LaravelMultiLanguage\Exceptions\ModelTranslationAlreadyExistsException;
10
11
trait Translatable
12
{
13
    abstract public function getKeyName();
14
15
    /**
16
     * @return void
17
     */
18
    public static function bootTranslatable(): void
19
    {
20
        static::creating(function ($model) {
21
            // set default language if not set
22
            if (!$model->{$model->getLangKey()}) {
23
                $model->{$model->getLangKey()} = App::getLocale();
24
            }
25
        });
26
27
        static::created(function ($model) {
28
            // Set original field id when created
29
            if (!$model->{$model->getForeignKey()}) {
30
                $model->{$model->getForeignKey()} = $model->id;
31
                $model->save();
32
            }
33
        });
34
35
        if (config('laravel-multi-language.apply_lang_global_scope')) {
36
            static::addGlobalScope(new LangScope);
37
        }
38
    }
39
40
    /**
41
     * @return string
42
     */
43
    public function getLangKey(): String
44
    {
45
        return config('laravel-multi-language.lang_key', 'lang');
46
    }
47
48
    /**
49
     * @return string
50
     */
51
    public function getForeignKey(): String
52
    {
53
        return config('laravel-multi-language.foreign_key', 'original_id');
54
    }
55
56
    /**
57
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
58
     */
59
    public function translations()
60
    {
61
        return $this->hasMany(get_class($this), $this->getForeignKey(), $this->getForeignKey());
0 ignored issues
show
Bug introduced by
It seems like hasMany() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
62
    }
63
64
    // public function original()
65
    // {
66
    //     return $this->belongsTo(get_class($this), 'profile_id', 'id');
67
    // }
68
69
    /**
70
     * Determin fields to be excluded from the translation
71
     *
72
     * @return array
73
     */
74
    private function fieldsToExclude(): array
75
    {
76
        $exclude = [];
77
        if ($this->excludeFieldsFromTranslation && is_array($this->excludeFieldsFromTranslation)) {
78
            $exclude = $this->excludeFieldsFromTranslation;
0 ignored issues
show
Bug introduced by
The property excludeFieldsFromTranslation does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
79
        }
80
81
        return array_merge(
82
            [$this->getKeyName(), $this->getLangKey(), $this->getForeignKey(), 'created_at', 'updated_at'],
83
            $exclude
84
        );
85
    }
86
87
    /**
88
     * Translate model to another language
89
     *
90
     * @param String $lang
91
     * @param array $data
92
     * @return Illuminate\Database\Eloquent\Model
93
     */
94
    public function translateTo($lang, $data = [])
95
    {
96
        $newLangData = [$this->getLangKey() => $lang, $this->getForeignKey() => $this->{$this->getForeignKey()}];
97
        $originalData = Arr::except($this->toArray(), $this->fieldsToExclude());
0 ignored issues
show
Bug introduced by
It seems like toArray() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
98
        $data = Arr::except($data, $this->fieldsToExclude()); // clean up passed data
99
        $data = array_merge($originalData, $data, $newLangData);
100
101
        if ($this->hasTranslation($lang)) {
102
            throw new ModelTranslationAlreadyExistsException('Translation already exists.', 1);
103
        }
104
105
        // TODO: add event here: model.translating
106
107
        $translation =  self::create($data);
108
109
        // TODO: add event here: model.translated
110
111
        return $translation;
112
    }
113
114
    /**
115
     * Checks if record has a  translation
116
     *
117
     * @param String $lang
118
     * @return Boolean
119
     */
120
    public function hasTranslation($lang): bool
121
    {
122
        if ($this->lang == $lang) {
0 ignored issues
show
Bug introduced by
The property lang does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
123
            return true;
124
        }
125
        return $this->getTranslationBaseQuery($lang)->exists();
0 ignored issues
show
Bug introduced by
The method exists() does not exist on Illuminate\Database\Eloquent\Builder. Did you maybe mean canUseExistsForExistenceCheck()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
126
    }
127
128
    /**
129
     * Undocumented function
130
     *
131
     * @param String $lang
132
     * @return \Illuminate\Database\Eloquent\Builder
133
     */
134
    private function getTranslationBaseQuery($lang): Builder
135
    {
136
        return self::withoutGlobalScope(LangScope::class)
137
            ->where($this->getLangKey(), $lang)
138
            ->where($this->getForeignKey(), $this->{$this->getForeignKey()});
139
    }
140
141
    /**
142
     * Get translation
143
     *
144
     * @param String $lang
145
     * @return Illuminate\Database\Eloquent\Model|null
146
     */
147
    public function translation($lang)
148
    {
149
        return $this->getTranslationBaseQuery($lang)->first();
150
    }
151
152
    /*
153
     * Return translation excluding the current language
154
     *
155
     * @return \Illuminate\Database\Eloquent\Builder|static
156
     */
157
    public function scopeWithTranslations(Builder $query, $lang = null, $fields = [])
158
    {
159
        return $query->with(['translations' => function ($q) use ($lang, $fields) {
160
            $q->notLang($lang, $fields);
161
        }]);
162
    }
163
164
    /**
165
     * @param \Illuminate\Database\Eloquent\Builder $query
166
     * @param string                                $lang
167
     *
168
     * @return \Illuminate\Database\Eloquent\Builder|static
169
     */
170
    public function scopeLang(Builder $query, $lang = null)
171
    {
172
        return $query->where($this->getLangKey(), $lang ?: App::getLocale());
173
    }
174
175
    /**
176
     * @param \Illuminate\Database\Eloquent\Builder $query
177
     * @param string                                $lang
178
     *
179
     * @return \Illuminate\Database\Eloquent\Builder|static
180
     */
181
    public function scopeNotLang(Builder $query, $lang = null)
182
    {
183
        return $query->where($this->getLangKey(), '!=', $lang ?: App::getLocale())
184
            ->withoutGlobalScope(LangScope::class);
185
    }
186
187
    /**
188
     * Return only original rows
189
     * @param \Illuminate\Database\Eloquent\Builder $query
190
     *
191
     * @return \Illuminate\Database\Eloquent\Builder|static
192
     */
193
    public function scopeOnlyOriginal(Builder $query)
194
    {
195
        return $query->whereRaw($this->getTable() . '.id = ' . $this->getTable() . '.' . $this->getForeignKey());
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?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
196
    }
197
198
    /* @param \Illuminate \Database \Eloquent \Builder $query
199
    *
200
    * @return \Illuminate\Database\Eloquent\Builder | static
201
    */
202
    public function scopeOnlyOriginals(Builder $query)
203
    {
204
        return $this->scopeOnlyOriginal($query);
205
    }
206
}
207