Изучите несколько методов и приемов Laravel Eloquent

задняя часть база данных SQL Laravel

Когда я впервые искал так называемый фреймворк Laravel, одной из моих целей было найти: самый простой способ манипулировать базой данных. Тогда цель остановилась на Eloquent ORM.

Сегодня я расскажу о некоторых методах Eloquent ORM, которые непросто найти и использовать.

1. Функции увеличения и уменьшения

Я обычно пишу:

$article = Article::find($article_id);
$article->read_count++;
$article->save();

использоватьincrementфункция

$article = Article::find($article_id);
$article->increment('read_count');

Конечно, вы можете передавать числа, а не просто увеличивать или уменьшать их на 1:

Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1

Давайте посмотрим, как реализован исходный код:

/**
 * Increment a column's value by a given amount.
 *
 * @param  string  $column
 * @param  int     $amount
 * @param  array   $extra
 * @return int
 */
public function increment($column, $amount = 1, array $extra = [])
{
    if (! is_numeric($amount)) {
        throw new InvalidArgumentException('Non-numeric value passed to increment method.');
    }

    $wrapped = $this->grammar->wrap($column);

    $columns = array_merge([$column => $this->raw("$wrapped + $amount")], $extra);

    return $this->update($columns);
}

/**
 * Decrement a column's value by a given amount.
 *
 * @param  string  $column
 * @param  int     $amount
 * @param  array   $extra
 * @return int
 */
public function decrement($column, $amount = 1, array $extra = [])
{
    if (! is_numeric($amount)) {
        throw new InvalidArgumentException('Non-numeric value passed to decrement method.');
    }

    $wrapped = $this->grammar->wrap($column);

    $columns = array_merge([$column => $this->raw("$wrapped - $amount")], $extra);

    return $this->update($columns);
}

основное использование$this->grammarРазберите поле $column и превратите его в исполняемый оператор sql.

/**
 * Wrap a value in keyword identifiers.
 *
 * @param  \Illuminate\Database\Query\Expression|string  $value
 * @param  bool    $prefixAlias
 * @return string
 */
public function wrap($value, $prefixAlias = false)
{
    if ($this->isExpression($value)) {
        return $this->getValue($value);
    }

    // If the value being wrapped has a column alias we will need to separate out
    // the pieces so we can wrap each of the segments of the expression on it
    // own, and then joins them both back together with the "as" connector.
    if (strpos(strtolower($value), ' as ') !== false) {
        return $this->wrapAliasedValue($value, $prefixAlias);
    }

    return $this->wrapSegments(explode('.', $value));
}

/**
 * Wrap the given value segments.
 *
 * @param  array  $segments
 * @return string
 */
protected function wrapSegments($segments)
{
    return collect($segments)->map(function ($segment, $key) use ($segments) {
        return $key == 0 && count($segments) > 1
                        ? $this->wrapTable($segment)
                        : $this->wrapValue($segment);
    })->implode('.');
}

/**
 * Wrap a single string in keyword identifiers.
 *
 * @param  string  $value
 * @return string
 */
protected function wrapValue($value)
{
    if ($value !== '*') {
        return '"'.str_replace('"', '""', $value).'"';
    }

    return $value;
}

Примечание: $grammerЭто абстрактный класс, и проект будет использовать разные$grammerНаследование класса для реализации функции запроса

Последний параметр$extra,потому чтоincrementФункция в конечном итоге будет выполненаupdate()метод, так что вы можете поместить дополнительные операторы, которые должны манипулировать данными в$extraв массиве.

2. WhereX

здесьwhereроль префикса,XОн представляет имя нашего поля, что может упростить написание нашего запроса, который обычно пишется так:

$users = User::where('approved', 1)->get();

Простое письмо:

$users = User::whereApproved(1)->get();

Конкретная реализация основного использования__callметод.

public mixed __call ( string name , arrayarguments )

public static mixed __callStatic ( string name , arrayarguments )

__call() вызывается, когда для объекта вызывается недоступный метод.

__callStatic() вызывается, когда недоступный метод вызывается в статическом контексте.

существуетQuery/Builder.phpЕго можно увидеть в:

/**
 * Handle dynamic method calls into the method.
 *
 * @param  string  $method
 * @param  array   $parameters
 * @return mixed
 *
 * @throws \BadMethodCallException
 */
public function __call($method, $parameters)
{
    if (static::hasMacro($method)) {
        return $this->macroCall($method, $parameters);
    }

    if (Str::startsWith($method, 'where')) {
        return $this->dynamicWhere($method, $parameters);
    }

    $className = static::class;

    throw new BadMethodCallException("Call to undefined method {$className}::{$method}()");
}

whereМетоды запроса все функции вызова:

return $this->dynamicWhere($method, $parameters);
/**
 * Handles dynamic "where" clauses to the query.
 *
 * @param  string  $method
 * @param  string  $parameters
 * @return $this
 */
public function dynamicWhere($method, $parameters)
{
    $finder = substr($method, 5);

    $segments = preg_split(
        '/(And|Or)(?=[A-Z])/', $finder, -1, PREG_SPLIT_DELIM_CAPTURE
    );

    // The connector variable will determine which connector will be used for the
    // query condition. We will change it as we come across new boolean values
    // in the dynamic method strings, which could contain a number of these.
    $connector = 'and';

    $index = 0;

    foreach ($segments as $segment) {
        // If the segment is not a boolean connector, we can assume it is a column's name
        // and we will add it to the query as a new constraint as a where clause, then
        // we can keep iterating through the dynamic method string's segments again.
        if ($segment !== 'And' && $segment !== 'Or') {
            $this->addDynamic($segment, $connector, $parameters, $index);

            $index++;
        }

        // Otherwise, we will store the connector so we know how the next where clause we
        // find in the query should be connected to the previous ones, meaning we will
        // have the proper boolean connector to connect the next where clause found.
        else {
            $connector = $segment;
        }
    }

    return $this;
}

Продолжай читатьaddDynamicфункция:

/**
 * Add a single dynamic where clause statement to the query.
 *
 * @param  string  $segment
 * @param  string  $connector
 * @param  array   $parameters
 * @param  int     $index
 * @return void
 */
protected function addDynamic($segment, $connector, $parameters, $index)
{
    // Once we have parsed out the columns and formatted the boolean operators we
    // are ready to add it to this query as a where clause just like any other
    // clause on the query. Then we'll increment the parameter index values.
    $bool = strtolower($connector);

    $this->where(Str::snake($segment), '=', $parameters[$index], $bool);
}

наконец вернулся$this->where(Str::snake($segment), '=', $parameters[$index], $bool);обычныйwhereпо приговору;

В то же время в этом процессе мы можем найтиwhereXметод, вы можете не только передавать в одно поле, но и передавать в несколько полей, использовать «И» или «Или» для соединения, а первая буква поля — заглавная «A ~ Z».

3. XorY methods

Слишком много способов писать в мирное время, сначала запросить, потом определить, существует ли, а уж потом решать, выводить или создавать.

Такие как:

$user = User::where('email', $email)->first();
if (!$user) {
  User::create([
    'email' => $email
  ]);
}

Одна строка кода для решения:

$user = User::firstOrCreate(['email' => $email]);

Примечание: вот еще одна функцияfirstOrNewа такжеfirstOrCreateАналогично, посмотрите на код:

/**
     * Get the first record matching the attributes or instantiate it.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function firstOrNew(array $attributes, array $values = [])
    {
        if (! is_null($instance = $this->where($attributes)->first())) {
            return $instance;
        }

        return $this->newModelInstance($attributes + $values);
    }

    /**
     * Get the first record matching the attributes or create it.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function firstOrCreate(array $attributes, array $values = [])
    {
        if (! is_null($instance = $this->where($attributes)->first())) {
            return $instance;
        }

        return tap($this->newModelInstance($attributes + $values), function ($instance) {
            $instance->save();
        });
    }

Основное отличие сценария: если вы найдете и создадите его под существующими атрибутами $, вы можете использоватьfirstOrCreate. Если нам нужно сначала запросить, а затем выполнить последующие операции с моделью, мы должны использоватьfirstOrNewметод, поместите операцию сохранения базы данных в конец, чтобы избежать повторного выполненияsave()метод.

4. find()

Функция FACE () получает данные через первичный ключ. Обычно он получает один данные. На самом деле, входящий параметр также может быть «массивом первичного ключа» для получения нескольких моделей.

$users = User::find([1,2,3]);

Посмотрим на реализацию его функции:

    /**
     * Find a model by its primary key.
     *
     * @param  mixed  $id
     * @param  array  $columns
     * @return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection|static[]|static|null
     */
    public function find($id, $columns = ['*'])
    {
        if (is_array($id) || $id instanceof Arrayable) {
            return $this->findMany($id, $columns);
        }

        return $this->whereKey($id)->first($columns);
    }

Первое, что нужно решить, является ли id массивом, и если да, то выполнить функцию findMany:

    /**
     * Find multiple models by their primary keys.
     *
     * @param  \Illuminate\Contracts\Support\Arrayable|array  $ids
     * @param  array  $columns
     * @return \Illuminate\Database\Eloquent\Collection
     */
    public function findMany($ids, $columns = ['*'])
    {
        if (empty($ids)) {
            return $this->model->newCollection();
        }

        return $this->whereKey($ids)->get($columns);
    }

Полученный результат является типом коллекции.

Суммировать

Во фреймворке Laravel есть много мест, на которые стоит обратить внимание, чтобы увидеть, как Laravel инкапсулирует методы. В Eloquent ORM также есть много способов увидеть, как реализуется исходный код один за другим.

Еще из этой статьи:Потяните Ravel-news.com/eloquent-mention…

Есть много функций, которые можно проанализировать, например:

Relationship with conditions and ordering

public function orders() {
    return $this->hasMany('App\Order');    
}

На самом деле, мы можем добавить операторы фильтра и сортировку при получении нескольких заказов. Например, получить оплату и вывести в порядке, обратном времени обновления:

public function paidOrders() {
    return $this->hasMany('App\Order')->where('paid', 1)->orderBy('updated_at');
}

"Продолжение следует"