Completed
Push — master ( b2d52f...7f76ce )
by Bruno
01:20
created

Translatable::scopeLang()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 2
nc 1
nop 2
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
    /**
65
     * Translate model to another language
66
     *
67
     * @param String $lang
68
     * @param array $data
69
     * @return Illuminate\Database\Eloquent\Model
70
     */
71
    public function translateTo($lang, $data = [])
72
    {
73
        $excludedFields = [$this->getKeyName(), $this->getLangKey(), $this->getForeignKey(), 'created_at', 'updated_at'];
74
        $newLangData = [$this->getLangKey() => $lang, $this->getForeignKey() => $this->{$this->getForeignKey()}];
75
        $originalData = Arr::except($this->toArray(), $excludedFields);
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...
76
        $data = Arr::except($data, $excludedFields); // clean up passed data
77
        $data = array_merge($originalData, $data, $newLangData);
78
79
        if ($this->hasTranslation($lang)) {
80
            throw new ModelTranslationAlreadyExistsException('Translation already exists.', 1);
81
        }
82
83
        // TODO: add event here: model.translating
84
85
        $translation =  self::create($data);
86
87
        // TODO: add event here: model.translated
88
89
        return $translation;
90
    }
91
92
    /**
93
     * Checks if record has a  translation
94
     *
95
     * @param String $lang
96
     * @return Boolean
97
     */
98
    public function hasTranslation($lang): bool
99
    {
100
        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...
101
            return true;
102
        }
103
        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...
104
    }
105
106
    /**
107
     * Undocumented function
108
     *
109
     * @param String $lang
110
     * @return \Illuminate\Database\Eloquent\Builder
111
     */
112
    private function getTranslationBaseQuery($lang): Builder
113
    {
114
        return self::withoutGlobalScope(LangScope::class)
115
            ->where($this->getLangKey(), $lang)
116
            ->where($this->getForeignKey(), $this->{$this->getForeignKey()});
117
    }
118
119
    /**
120
     * Get translation
121
     *
122
     * @param String $lang
123
     * @return Illuminate\Database\Eloquent\Model|null
124
     */
125
    public function translation($lang)
126
    {
127
        return $this->getTranslationBaseQuery($lang)->first();
128
    }
129
130
    /*
131
     * Return translation excluding the current language
132
     *
133
     * @return \Illuminate\Database\Eloquent\Builder|static
134
     */
135
    public function scopeWithTranslations(Builder $query, $lang = null, $fields = [])
136
    {
137
        return $query->with(['translations' => function ($q) use ($lang, $fields) {
138
            $q->notLang($lang, $fields);
139
        }]);
140
    }
141
142
    /**
143
     * @param \Illuminate\Database\Eloquent\Builder $query
144
     * @param string                                $lang
145
     *
146
     * @return \Illuminate\Database\Eloquent\Builder|static
147
     */
148
    public function scopeLang(Builder $query, $lang = null)
149
    {
150
        return $query->where($this->getLangKey(), $lang ?: App::getLocale());
151
    }
152
153
    /**
154
     * @param \Illuminate\Database\Eloquent\Builder $query
155
     * @param string                                $lang
156
     *
157
     * @return \Illuminate\Database\Eloquent\Builder|static
158
     */
159
    public function scopeNotLang(Builder $query, $lang = null)
160
    {
161
        return $query->where($this->getLangKey(), '!=', $lang ?: App::getLocale())
162
            ->withoutGlobalScope(LangScope::class);
163
    }
164
165
    /**
166
     * Return only original rows
167
     * @param \Illuminate\Database\Eloquent\Builder $query
168
     *
169
     * @return \Illuminate\Database\Eloquent\Builder|static
170
     */
171
    public function scopeOnlyOriginal(Builder $query)
172
    {
173
        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...
174
    }
175
176
    /* @param \Illuminate \Database \Eloquent \Builder $query
177
    *
178
    * @return \Illuminate\Database\Eloquent\Builder | static
179
    */
180
    public function scopeOnlyOriginals(Builder $query)
181
    {
182
        return $this->scopeOnlyOriginal($query);
183
    }
184
}
185