moveLangAttributes(); }); static::saved(function ($model) { $model->langSave(); $model->moveLangAttributes(false); }); static::deleting(function ($model) { $model->langDelete(); }); } /** * Join lang table (according to $columns) * * @param QueryBuilder $query * @param array $columns * @param string|null $tableTo table we join languages table to * @return array result columns */ public function joinLangs($query, array $columns = ['*'], $tableTo = null) { $table = $this->langTable(); $join = true; if (! $tableTo) { $tableTo = $this->getTable(); if ($columns === ['*']) { if ($query->hasColumns()) { $columns = $query->getColumns(); } } if ($columns === ['*'] || $columns === [$tableTo . '.*']) { $columns = ['*']; } elseif (in_array($tableTo . '.*', $columns, true)) { # merge case $columns[] = $table . '.*'; } else { $join = false; foreach ($columns as $key => $column) { if ($this->isLangColumn($column)) { $join = true; } $columns[$key] = $this->prefixColumn($column); } if ($join) { $key = $this->getQualifiedKeyName(); if (! in_array($key, $columns, true)) { array_unshift($columns, $key); } } } } if ($join) { # Join languages table $model = $this; $query->leftJoin($table, function ($leftJoin) use ($table, $tableTo, $model) { $leftJoin->on($table . '.id', '=', $tableTo . '.' . $model->getKeyName()); $leftJoin->where($table . '.' . $model->langColumnName(), '=', $model->langDefault()); }); } return $columns; } /** * Languages table * {table}_lang by default * * @return string */ public function langTable() { return $this->getTable() . '_lang'; } /** * Language column to store language key * * @return string */ public function langColumnName() { return 'lang'; } /** * Add table prefix to column name * * @param string|array $column * @param string|null $table table to prefix with * @return string */ public function prefixColumn($column, $table = null) { if (is_string($column) && $this->isLangColumn($column)) { return (mb_strpos($column, '.') === false ? $this->langTable() . '.' . $column : $column); } return parent::prefixColumn($column, $table); } /** * Load translatable data * * @param string|array|null $languageKey languages only */ public function langLoad($languageKey = null) { $data = []; $idColumn = $this->getKeyName(); $langColumn = $this->langColumnName(); $langColumns = $this->langColumns(); if (empty($langColumns)) { return; } $rows = $this->newBaseQueryBuilder()->select([$idColumn,$langColumn])->addSelect($langColumns) ->from($this->langTable())->where($idColumn, $this->getKey()) ->when($languageKey, function ($query) use ($langColumn, $languageKey) { $query->where($langColumn, $languageKey); }) ->get(); if ($rows) { foreach ($rows as $row) { foreach ($row as $key => $value) { if (in_array($key, $langColumns, true)) { $data[$key][$row[$langColumn]] = $value; } } } } if (! empty($data)) { foreach ($data as $col => $values) { $this->setAttribute($col, $values); } $this->syncOriginalAttributes(array_keys($data)); } } /** * Save translatable data (from attributes) * * @return void */ public function langSave() { $columns = $this->langColumns(); if (empty($columns)) { return; } $langKey = $this->langColumnName(); $langs = $this->langsList(); $attributes = $this->langAttributes; // to array $langCurrent = ($this->getAttribute('lang') ?? $this->langDefault()); foreach ($columns as $col) { if (array_key_exists($col, $attributes) && is_string($attributes[$col])) { if ($this->getOriginal($col) !== $attributes[$col]) { $attributes[$col] = [$langCurrent => $attributes[$col]]; } else { $attributes[$col] = []; } } } $insert = []; $where = []; $where[$this->getKeyName()] = $this->getKey(); foreach ($langs as $lang) { $update = []; foreach ($columns as $col) { if ( (! isset($this->original[$col]) || ($this->original[$col] !== $attributes[$col] ?? [])) && isset($attributes[$col][$lang]) ) { $update[$col] = $attributes[$col][$lang]; } } if (! empty($update)) { $where[$langKey] = $lang; if ($this->wasRecentlyCreated) { $insert[] = array_merge($where, $update); } else { $res = $this->newBaseQueryBuilder()->from($this->langTable())->where($where)->update($update); if (empty($res)) { $insert[] = array_merge($where, $update); } } } } if ($this->wasRecentlyCreated && empty($insert)) { $insert = []; foreach ($langs as $lang) { $i[$this->getKeyName()] = $this->getKey(); $i[$langKey] = $lang; $insert[] = $i; } } if (! empty($insert)) { $this->newBaseQueryBuilder()->from($this->langTable())->insert($insert); } } /** * Delete translatable data * * @return void */ public function langDelete() { $columns = $this->langColumns(); if (empty($columns)) { return; } $this->newBaseQueryBuilder()->from($this->langTable()) ->where($this->getKeyName(), $this->getKey())->delete(); } }