Когда я впервые искал так называемый фреймворк 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 arguments )
public static mixed __callStatic ( string arguments )
__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');
}
"Продолжение следует"