Completed
Push — master ( d4c3f4...d53235 )
by Bruno
01:11
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
    /**
14
     * @return void
15
     */
16
    public static function bootTranslatable(): void
17
    {
18
        static::creating(function ($model) {
19
            // set default language if not set
20
            if (!$model->{$model->getLangKey()}) {
21
                $model->{$model->getLangKey()} = App::getLocale();
22
            }
23
        });
24
25
        static::created(function ($model) {
26
            // Set original field id when created
27
            if (!$model->{$model->getForeignKey()}) {
28
                $model->{$model->getForeignKey()} = $model->id;
29
                $model->save();
30
            }
31
        });
32
33
        if (config('laravel-multi-language.apply_lang_global_scope')) {
34
            static::addGlobalScope(new LangScope);
35
        }
36
    }
37
38
    /**
39
     * @return string
40
     */
41
    public function getLangKey(): String
42
    {
43
        return config('laravel-multi-language.lang_key', 'lang');
44
    }
45
46
    /**
47
     * @return string
48
     */
49
    public function getForeignKey(): String
50
    {
51
        return config('laravel-multi-language.foreign_key', 'original_id');
52
    }
53
54
    /**
55
     * @return \Illuminate\Database\Eloquent\Relations\HasMany
56
     */
57
    public function translations()
58
    {
59
        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...
60
    }
61
62
    /**
63
     * Translate model to another language
64
     *
65
     * @param String $lang
66
     * @param array $data
67
     * @return Illuminate\Database\Eloquent\Model
68
     */
69
    public function translateTo($lang, $data = [])
70
    {
71
        $excludedFields = ['id', 'lang', 'original_id', 'created_at', 'updated_at'];
72
        $newLangData = ['lang' => $lang, 'original_id' => $this->original_id];
0 ignored issues
show
Bug introduced by
The property original_id 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...
73
        $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...
74
        $data = Arr::except($data, $excludedFields); // clean up passed data
75
        $data = array_merge($originalData, $data, $newLangData);
76
77
        if ($this->hasTranslation($lang)) {
78
            throw new ModelTranslationAlreadyExistsException('Translation already exists.', 1);
79
        }
80
81
        // TODO: add event here: model.translating
82
83
        $translation =  self::create($data);
84
85
        // TODO: add event here: model.translated
86
87
        return $translation;
88
    }
89
90
    /**
91
     * Checks if record has a  translation
92
     *
93
     * @param String $lang
94
     * @return Model|null
95
     */
96
    public function hasTranslation($lang)
97
    {
98
        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...
99
            return true;
100
        }
101
102
        return self::withoutGlobalScope(LangScope::class)
103
            ->where('lang', $lang)
104
            ->where('original_id', $this->original_id)
105
            ->exists();
106
    }
107
108
    /**
109
     * Get translation
110
     *
111
     * @param String $lang
112
     * @return Model|null
113
     */
114
    public function translation($lang)
115
    {
116
        return self::withoutGlobalScope(LangScope::class)
117
            ->where('lang', $lang)
118
            ->where('original_id', $this->original_id)
119
            ->first();
120
    }
121
122
    /*
123
     * Return translation excluding the current language
124
     *
125
     * @return \Illuminate\Database\Eloquent\Builder|static
126
     */
127
    public function scopeWithTranslations(Builder $query, $lang = null, $fields = [])
128
    {
129
        return $query->with(['translations' => function ($q) use ($lang, $fields) {
130
            $q->notLang($lang, $fields);
131
        }]);
132
    }
133
134
    /**
135
     * @param \Illuminate\Database\Eloquent\Builder $query
136
     * @param string                                $lang
137
     *
138
     * @return \Illuminate\Database\Eloquent\Builder|static
139
     */
140
    public function scopeLang(Builder $query, $lang = null)
141
    {
142
        return $query->where($this->getLangKey(), $lang ?: App::getLocale());
143
    }
144
145
    /**
146
     * @param \Illuminate\Database\Eloquent\Builder $query
147
     * @param string                                $lang
148
     *
149
     * @return \Illuminate\Database\Eloquent\Builder|static
150
     */
151
    public function scopeNotLang(Builder $query, $lang = null)
152
    {
153
        return $query->where($this->getLangKey(), '!=', $lang ?: App::getLocale())
154
            ->withoutGlobalScope(LangScope::class);
155
    }
156
157
    /**
158
     * Return only original rows
159
     * @param \Illuminate\Database\Eloquent\Builder $query
160
     *
161
     * @return \Illuminate\Database\Eloquent\Builder|static
162
     */
163
    public function scopeOnlyOriginal(Builder $query)
164
    {
165
        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...
166
    }
167
168
    /* @param \Illuminate \Database \Eloquent \Builder $query
169
    *
170
    * @return \Illuminate\Database\Eloquent\Builder | static
171
    */
172
    public function scopeOnlyOriginals(Builder $query)
173
    {
174
        return $this->scopeOnlyOriginal($query);
175
    }
176
}
177