laravel Eloquent ORM

来源:互联网 发布:微软数据库工程师 编辑:程序博客网 时间:2024/05/17 23:37

Eloquent 是 Laravel 的 'ORM',即 'Object Relational Mapping',对象关系映射。ORM 的出现是为了帮我们把对数据库的操作变得更加地方便。

Eloquent 让一个 'Model类' 对应一张数据库表,并且在底层封装了很多 'function',可以让 Model 类非常方便地调用。

在实际的使用过程中,我们在model里面定义一个class来继承Eloquent(它的原始文件其实是Model.php文件,只不过在自动加载里面被修改了名字,源文件在Illuminate\Datebase\Eloquent\Model 里面,自动加载类的定义在app\config\app   

'Eloquent'        => 'Illuminate\Database\Eloquent\Model',

Illuminate\Database\Query\Builder 
这个Builder类,定义了数据查询的所有的中间过程
<span style="color:#6600CC;">class Builder {/** * The database connection instance. * * @var \Illuminate\Database\Connection */protected $connection;/** * The database query grammar instance. * * @var \Illuminate\Database\Query\Grammars\Grammar */protected $grammar;/** * The database query post processor instance. * * @var \Illuminate\Database\Query\Processors\Processor */protected $processor;/** * The current query value bindings. * * @var array */protected $bindings = array();/** * An aggregate function and column to be run. * * @var array */public $aggregate;/** * The columns that should be returned. * * @var array */public $columns;/** * Indicates if the query returns distinct results. * * @var bool */public $distinct = false;/** * The table which the query is targeting. * * @var string */public $from;/** * The table joins for the query. * * @var array */public $joins;/** * The where constraints for the query. * * @var array */public $wheres;/** * The groupings for the query. * * @var array */public $groups;/** * The having constraints for the query. * * @var array */public $havings;/** * The orderings for the query. * * @var array */public $orders;/** * The maximum number of records to return. * * @var int */public $limit;/** * The number of records to skip. * * @var int */public $offset;/** * The query union statements. * * @var array */public $unions;/** * The key that should be used when caching the query. * * @var string */protected $cacheKey;/** * The number of minutes to cache the query. * * @var int */protected $cacheMinutes;/** * All of the available clause operators. * * @var array */protected $operators = array('=', '<', '>', '<=', '>=', '<>', '!=','like', 'not like', 'between', 'ilike','&', '|', '^', '<<', '>>',);/** * Create a new query builder instance. * * @param  \Illuminate\Database\ConnectionInterface  $connection * @param  \Illuminate\Database\Query\Grammars\Grammar  $grammar * @param  \Illuminate\Database\Query\Processors\Processor  $processor * @return void */public function __construct(ConnectionInterface $connection,                                Grammar $grammar,                                Processor $processor){$this->grammar = $grammar;$this->processor = $processor;$this->connection = $connection;}/** * Set the columns to be selected. * * @param  array  $columns * @return \Illuminate\Database\Query\Builder|static */public function select($columns = array('*')){$this->columns = is_array($columns) ? $columns : func_get_args();return $this;}/** * Add a new select column to the query. * * @param  mixed  $column * @return \Illuminate\Database\Query\Builder|static */public function addSelect($column){$column = is_array($column) ? $column : func_get_args();$this->columns = array_merge((array) $this->columns, $column);return $this;}/** * Force the query to only return distinct results. * * @return \Illuminate\Database\Query\Builder|static */public function distinct(){$this->distinct = true;return $this;}/** * Set the table which the query is targeting. * * @param  string  $table * @return \Illuminate\Database\Query\Builder|static */public function from($table){$this->from = $table;return $this;}/** * Add a join clause to the query. * * @param  string  $table * @param  string  $first * @param  string  $operator * @param  string  $second * @param  string  $type * @return \Illuminate\Database\Query\Builder|static */public function join($table, $first, $operator = null, $second = null, $type = 'inner'){// If the first "column" of the join is really a Closure instance the developer// is trying to build a join with a complex "on" clause containing more than// one condition, so we'll add the join and call a Closure with the query.if ($first instanceof Closure){$this->joins[] = new JoinClause($type, $table);call_user_func($first, end($this->joins));}// If the column is simply a string, we can assume the join simply has a basic// "on" clause with a single condition. So we will just build the join with// this simple join clauses attached to it. There is not a join callback.else{$join = new JoinClause($type, $table);$join->on($first, $operator, $second);$this->joins[] = $join;}return $this;}/** * Add a left join to the query. * * @param  string  $table * @param  string  $first * @param  string  $operator * @param  string  $second * @return \Illuminate\Database\Query\Builder|static */public function leftJoin($table, $first, $operator = null, $second = null){return $this->join($table, $first, $operator, $second, 'left');}/** * Add a basic where clause to the query. * * @param  string  $column * @param  string  $operator * @param  mixed   $value * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function where($column, $operator = null, $value = null, $boolean = 'and'){if ($this->invalidOperatorAndValue($operator, $value)){throw new \InvalidArgumentException("Value must be provided.");}// If the columns is actually a Closure instance, we will assume the developer// wants to begin a nested where statement which is wrapped in parenthesis.// We'll add that Closure to the query then return back out immediately.if ($column instanceof Closure){return $this->whereNested($column, $boolean);}// If the given operator is not found in the list of valid operators we will// assume that the developer is just short-cutting the '=' operators and// we will set the operators to '=' and set the values appropriately.if ( ! in_array(strtolower($operator), $this->operators, true)){list($value, $operator) = array($operator, '=');}// If the value is a Closure, it means the developer is performing an entire// sub-select within the query and we will need to compile the sub-select// within the where clause to get the appropriate query record results.if ($value instanceof Closure){return $this->whereSub($column, $operator, $value, $boolean);}// If the value is "null", we will just assume the developer wants to add a// where null clause to the query. So, we will allow a short-cut here to// that method for convenience so the developer doesn't have to check.if (is_null($value)){return $this->whereNull($column, $boolean, $operator != '=');}// Now that we are working with just a simple query we can put the elements// in our array and add the query binding to our array of bindings that// will be bound to each SQL statements when it is finally executed.$type = 'Basic';$this->wheres[] = compact('type', 'column', 'operator', 'value', 'boolean');if ( ! $value instanceof Expression){$this->bindings[] = $value;}return $this;}/** * Add an "or where" clause to the query. * * @param  string  $column * @param  string  $operator * @param  mixed   $value * @return \Illuminate\Database\Query\Builder|static */public function orWhere($column, $operator = null, $value = null){return $this->where($column, $operator, $value, 'or');}/** * Determine if the given operator and value combination is legal. * * @param  string  $operator * @param  mxied  $value * @return bool */protected function invalidOperatorAndValue($operator, $value){$isOperator = in_array($operator, $this->operators);return ($isOperator and $operator != '=' and is_null($value));}/** * Add a raw where clause to the query. * * @param  string  $sql * @param  array   $bindings * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereRaw($sql, array $bindings = array(), $boolean = 'and'){$type = 'raw';$this->wheres[] = compact('type', 'sql', 'boolean');$this->bindings = array_merge($this->bindings, $bindings);return $this;}/** * Add a raw or where clause to the query. * * @param  string  $sql * @param  array   $bindings * @return \Illuminate\Database\Query\Builder|static */public function orWhereRaw($sql, array $bindings = array()){return $this->whereRaw($sql, $bindings, 'or');}/** * Add a where between statement to the query. * * @param  string  $column * @param  array   $values * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereBetween($column, array $values, $boolean = 'and'){$type = 'between';$this->wheres[] = compact('column', 'type', 'boolean');$this->bindings = array_merge($this->bindings, $values);return $this;}/** * Add an or where between statement to the query. * * @param  string  $column * @param  array   $values * @return \Illuminate\Database\Query\Builder|static */public function orWhereBetween($column, array $values){return $this->whereBetween($column, $values, 'or');}/** * Add a nested where statement to the query. * * @param  \Closure $callback * @param  string   $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereNested(Closure $callback, $boolean = 'and'){// To handle nested queries we'll actually create a brand new query instance// and pass it off to the Closure that we have. The Closure can simply do// do whatever it wants to a query then we will store it for compiling.$type = 'Nested';$query = $this->newQuery();$query->from($this->from);call_user_func($callback, $query);// Once we have let the Closure do its things, we can gather the bindings on// the nested query builder and merge them into these bindings since they// need to get extracted out of the children and assigned to the array.if (count($query->wheres)){$this->wheres[] = compact('type', 'query', 'boolean');$this->mergeBindings($query);}return $this;}/** * Add a full sub-select to the query. * * @param  string   $column * @param  string   $operator * @param  \Closure $callback * @param  string   $boolean * @return \Illuminate\Database\Query\Builder|static */protected function whereSub($column, $operator, Closure $callback, $boolean){$type = 'Sub';$query = $this->newQuery();// Once we have the query instance we can simply execute it so it can add all// of the sub-select's conditions to itself, and then we can cache it off// in the array of where clauses for the "main" parent query instance.call_user_func($callback, $query);$this->wheres[] = compact('type', 'column', 'operator', 'query', 'boolean');$this->mergeBindings($query);return $this;}/** * Add an exists clause to the query. * * @param  \Closure $callback * @param  string   $boolean * @param  bool     $not * @return \Illuminate\Database\Query\Builder|static */public function whereExists(Closure $callback, $boolean = 'and', $not = false){$type = $not ? 'NotExists' : 'Exists';$query = $this->newQuery();// Similar to the sub-select clause, we will create a new query instance so// the developer may cleanly specify the entire exists query and we will// compile the whole thing in the grammar and insert it into the SQL.call_user_func($callback, $query);$this->wheres[] = compact('type', 'operator', 'query', 'boolean');$this->mergeBindings($query);return $this;}/** * Add an or exists clause to the query. * * @param  \Closure $callback * @param  bool     $not * @return \Illuminate\Database\Query\Builder|static */public function orWhereExists(Closure $callback, $not = false){return $this->whereExists($callback, 'or', $not);}/** * Add a where not exists clause to the query. * * @param  \Closure $callback * @param  string   $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereNotExists(Closure $callback, $boolean = 'and'){return $this->whereExists($callback, $boolean, true);}/** * Add a where not exists clause to the query. * * @param  \Closure  $callback * @return \Illuminate\Database\Query\Builder|static */public function orWhereNotExists(Closure $callback){return $this->orWhereExists($callback, true);}/** * Add a "where in" clause to the query. * * @param  string  $column * @param  mixed   $values * @param  string  $boolean * @param  bool    $not * @return \Illuminate\Database\Query\Builder|static */public function whereIn($column, $values, $boolean = 'and', $not = false){$type = $not ? 'NotIn' : 'In';// If the value of the where in clause is actually a Closure, we will assume that// the developer is using a full sub-select for this "in" statement, and will// execute those Closures, then we can re-construct the entire sub-selects.if ($values instanceof Closure){return $this->whereInSub($column, $values, $boolean, $not);}$this->wheres[] = compact('type', 'column', 'values', 'boolean');$this->bindings = array_merge($this->bindings, $values);return $this;}/** * Add an "or where in" clause to the query. * * @param  string  $column * @param  mixed   $values * @return \Illuminate\Database\Query\Builder|static */public function orWhereIn($column, $values){return $this->whereIn($column, $values, 'or');}/** * Add a "where not in" clause to the query. * * @param  string  $column * @param  mixed   $values * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereNotIn($column, $values, $boolean = 'and'){return $this->whereIn($column, $values, $boolean, true);}/** * Add an "or where not in" clause to the query. * * @param  string  $column * @param  mixed   $values * @return \Illuminate\Database\Query\Builder|static */public function orWhereNotIn($column, $values){return $this->whereNotIn($column, $values, 'or');}/** * Add a where in with a sub-select to the query. * * @param  string   $column * @param  \Closure $callback * @param  string   $boolean * @param  bool     $not * @return \Illuminate\Database\Query\Builder|static */protected function whereInSub($column, Closure $callback, $boolean, $not){$type = $not ? 'NotInSub' : 'InSub';// To create the exists sub-select, we will actually create a query and call the// provided callback with the query so the developer may set any of the query// conditions they want for the in clause, then we'll put it in this array.call_user_func($callback, $query = $this->newQuery());$this->wheres[] = compact('type', 'column', 'query', 'boolean');$this->mergeBindings($query);return $this;}/** * Add a "where null" clause to the query. * * @param  string  $column * @param  string  $boolean * @param  bool    $not * @return \Illuminate\Database\Query\Builder|static */public function whereNull($column, $boolean = 'and', $not = false){$type = $not ? 'NotNull' : 'Null';$this->wheres[] = compact('type', 'column', 'boolean');return $this;}/** * Add an "or where null" clause to the query. * * @param  string  $column * @return \Illuminate\Database\Query\Builder|static */public function orWhereNull($column){return $this->whereNull($column, 'or');}/** * Add a "where not null" clause to the query. * * @param  string  $column * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function whereNotNull($column, $boolean = 'and'){return $this->whereNull($column, $boolean, true);}/** * Add an "or where not null" clause to the query. * * @param  string  $column * @return \Illuminate\Database\Query\Builder|static */public function orWhereNotNull($column){return $this->whereNotNull($column, 'or');}/** * Handles dynamic "where" clauses to the query. * * @param  string  $method * @param  string  $parameters * @return \Illuminate\Database\Query\Builder|static */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' 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;}/** * 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(snake_case($segment), '=', $parameters[$index], $bool);}/** * Add a "group by" clause to the query. * * @param  dynamic  $columns * @return \Illuminate\Database\Query\Builder|static */public function groupBy(){$this->groups = array_merge((array) $this->groups, func_get_args());return $this;}/** * Add a "having" clause to the query. * * @param  string  $column * @param  string  $operator * @param  string  $value * @return \Illuminate\Database\Query\Builder|static */public function having($column, $operator = null, $value = null){$type = 'basic';$this->havings[] = compact('type', 'column', 'operator', 'value');$this->bindings[] = $value;return $this;}/** * Add a raw having clause to the query. * * @param  string  $sql * @param  array   $bindings * @param  string  $boolean * @return \Illuminate\Database\Query\Builder|static */public function havingRaw($sql, array $bindings = array(), $boolean = 'and'){$type = 'raw';$this->havings[] = compact('type', 'sql', 'boolean');$this->bindings = array_merge($this->bindings, $bindings);return $this;}/** * Add a raw or having clause to the query. * * @param  string  $sql * @param  array   $bindings * @return \Illuminate\Database\Query\Builder|static */public function orHavingRaw($sql, array $bindings = array()){return $this->havingRaw($sql, $bindings, 'or');}/** * Add an "order by" clause to the query. * * @param  string  $column * @param  string  $direction * @return \Illuminate\Database\Query\Builder|static */public function orderBy($column, $direction = 'asc'){$direction = strtolower($direction) == 'asc' ? 'asc' : 'desc';$this->orders[] = compact('column', 'direction');return $this;}/** * Add a raw "order by" clause to the query. * * @param  string  $sql * @param  array  $bindings * @return \Illuminate\Database\Query\Builder|static */public function orderByRaw($sql, $bindings = array()){$type = 'raw';$this->orders[] = compact('type', 'sql');$this->bindings = array_merge($this->bindings, $bindings);return $this;}/** * Set the "offset" value of the query. * * @param  int  $value * @return \Illuminate\Database\Query\Builder|static */public function offset($value){$this->offset = $value;return $this;}/** * Alias to set the "offset" value of the query. * * @param  int  $value * @return \Illuminate\Database\Query\Builder|static */public function skip($value){return $this->offset($value);}/** * Set the "limit" value of the query. * * @param  int  $value * @return \Illuminate\Database\Query\Builder|static */public function limit($value){if ($value > 0) $this->limit = $value;return $this;}/** * Alias to set the "limit" value of the query. * * @param  int  $value * @return \Illuminate\Database\Query\Builder|static */public function take($value){return $this->limit($value);}/** * Set the limit and offset for a given page. * * @param  int  $page * @param  int  $perPage * @return \Illuminate\Database\Query\Builder|static */public function forPage($page, $perPage = 15){return $this->skip(($page - 1) * $perPage)->take($perPage);}/** * Add a union statement to the query. * * @param  \Illuminate\Database\Query\Builder|\Closure  $query * @param  bool $all * @return \Illuminate\Database\Query\Builder|static */public function union($query, $all = false){if ($query instanceof Closure){call_user_func($query, $query = $this->newQuery());}$this->unions[] = compact('query', 'all');return $this->mergeBindings($query);}/** * Add a union all statement to the query. * * @param  \Illuminate\Database\Query\Builder|\Closure  $query * @return \Illuminate\Database\Query\Builder|static */public function unionAll($query){return $this->union($query, true);}/** * Get the SQL representation of the query. * * @return string */public function toSql(){return $this->grammar->compileSelect($this);}/** * Indicate that the query results should be cached. * * @param  int  $minutes * @param  string  $key * @return \Illuminate\Database\Query\Builder|static */public function remember($minutes, $key = null){list($this->cacheMinutes, $this->cacheKey) = array($minutes, $key);return $this;}/** * Execute a query for a single record by ID. * * @param  int    $id * @param  array  $columns * @return mixed|static */public function find($id, $columns = array('*')){return $this->where('id', '=', $id)->first($columns);}/** * Pluck a single column's value from the first result of a query. * * @param  string  $column * @return mixed */public function pluck($column){$result = (array) $this->first(array($column));return count($result) > 0 ? reset($result) : null;}/** * Execute the query and get the first result. * * @param  array   $columns * @return mixed|static */public function first($columns = array('*')){$results = $this->take(1)->get($columns);return count($results) > 0 ? reset($results) : null;}/** * Execute the query as a "select" statement. * * @param  array  $columns * @return array|static[] */public function get($columns = array('*')){if ( ! is_null($this->cacheMinutes)) return $this->getCached($columns);return $this->getFresh($columns);}/** * Execute the query as a fresh "select" statement. * * @param  array  $columns * @return array|static[] */public function getFresh($columns = array('*')){if (is_null($this->columns)) $this->columns = $columns;return $this->processor->processSelect($this, $this->runSelect());}/** * Run the query as a "select" statement against the connection. * * @return array */protected function runSelect(){return $this->connection->select($this->toSql(), $this->bindings);}/** * Execute the query as a cached "select" statement. * * @param  array  $columns * @return array */public function getCached($columns = array('*')){if (is_null($this->columns)) $this->columns = $columns;list($key, $minutes) = $this->getCacheInfo();// If the query is requested ot be cached, we will cache it using a unique key// for this database connection and query statement, including the bindings// that are used on this query, providing great convenience when caching.$cache = $this->connection->getCacheManager();$callback = $this->getCacheCallback($columns);return $cache->remember($key, $minutes, $callback);}/** * Get the cache key and cache minutes as an array. * * @return array */protected function getCacheInfo(){return array($this->getCacheKey(), $this->cacheMinutes);}/** * Get a unique cache key for the complete query. * * @return string */public function getCacheKey(){return $this->cacheKey ?: $this->generateCacheKey();}/** * Generate the unique cache key for the query. * * @return string */public function generateCacheKey(){$name = $this->connection->getName();return md5($name.$this->toSql().serialize($this->bindings));}/** * Get the Closure callback used when caching queries. * * @param  array  $columns * @return \Closure */protected function getCacheCallback($columns){$me = $this;return function() use ($me, $columns) { return $me->getFresh($columns); };}/** * Chunk the results of the query. * * @param  int  $count * @param  callable  $callback * @return void */public function chunk($count, $callback){$results = $this->forPage($page = 1, $count)->get();while (count($results) > 0){// On each chunk result set, we will pass them to the callback and then let the// developer take care of everything within the callback, which allows us to// keep the memory low for spinning through large result sets for working.call_user_func($callback, $results);$page++;$results = $this->forPage($page, $count)->get();}}/** * Get an array with the values of a given column. * * @param  string  $column * @param  string  $key * @return array */public function lists($column, $key = null){$columns = $this->getListSelect($column, $key);// First we will just get all of the column values for the record result set// then we can associate those values with the column if it was specified// otherwise we can just give these values back without a specific key.$results = new Collection($this->get($columns));$values = $results->fetch($columns[0])->all();// If a key was specified and we have results, we will go ahead and combine// the values with the keys of all of the records so that the values can// be accessed by the key of the rows instead of simply being numeric.if ( ! is_null($key) and count($results) > 0){$keys = $results->fetch($key)->all();return array_combine($keys, $values);}return $values;}/** * Get the columns that should be used in a list array. * * @param  string  $column * @param  string  $key * @return array */protected function getListSelect($column, $key){$select = is_null($key) ? array($column) : array($column, $key);// If the selected column contains a "dot", we will remove it so that the list// operation can run normally. Specifying the table is not needed, since we// really want the names of the columns as it is in this resulting array.if (($dot = strpos($select[0], '.')) !== false){$select[0] = substr($select[0], $dot + 1);}return $select;}/** * Concatenate values of a given column as a string. * * @param  string  $column * @param  string  $glue * @return string */public function implode($column, $glue = null){if (is_null($glue)) return implode($this->lists($column));return implode($glue, $this->lists($column));}/** * Get a paginator for the "select" statement. * * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */public function paginate($perPage = 15, $columns = array('*')){$paginator = $this->connection->getPaginator();if (isset($this->groups)){return $this->groupedPaginate($paginator, $perPage, $columns);}else{return $this->ungroupedPaginate($paginator, $perPage, $columns);}}/** * Create a paginator for a grouped pagination statement. * * @param  \Illuminate\Pagination\Environment  $paginator * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */protected function groupedPaginate($paginator, $perPage, $columns){$results = $this->get($columns);return $this->buildRawPaginator($paginator, $results, $perPage);}/** * Build a paginator instance from a raw result array. * * @param  \Illuminate\Pagination\Environment  $paginator * @param  array  $results * @param  int    $perPage * @return \Illuminate\Pagination\Paginator */public function buildRawPaginator($paginator, $results, $perPage){// For queries which have a group by, we will actually retrieve the entire set// of rows from the table and "slice" them via PHP. This is inefficient and// the developer must be aware of this behavior; however, it's an option.$start = ($paginator->getCurrentPage() - 1) * $perPage;$sliced = array_slice($results, $start, $perPage);return $paginator->make($sliced, count($results), $perPage);}/** * Create a paginator for an un-grouped pagination statement. * * @param  \Illuminate\Pagination\Environment  $paginator * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */protected function ungroupedPaginate($paginator, $perPage, $columns){$total = $this->getPaginationCount();// Once we have the total number of records to be paginated, we can grab the// current page and the result array. Then we are ready to create a brand// new Paginator instances for the results which will create the links.$page = $paginator->getCurrentPage($total);$results = $this->forPage($page, $perPage)->get($columns);return $paginator->make($results, $total, $perPage);}/** * Get the count of the total records for pagination. * * @return int */public function getPaginationCount(){list($orders, $this->orders) = array($this->orders, null);$columns = $this->columns;// Because some database engines may throw errors if we leave the ordering// statements on the query, we will "back them up" and remove them from// the query. Once we have the count we will put them back onto this.$total = $this->count();$this->orders = $orders;// Once the query is run we need to put the old select columns back on the// instance so that the select query will run properly. Otherwise, they// will be cleared, then the query will fire with all of the columns.$this->columns = $columns;return $total;}/** * Determine if any rows exist for the current query. * * @return bool */public function exists(){return $this->count() > 0;}/** * Retrieve the "count" result of the query. * * @param  string  $column * @return int */public function count($column = '*'){return $this->aggregate(__FUNCTION__, array($column));}/** * Retrieve the minimum value of a given column. * * @param  string  $column * @return mixed */public function min($column){return $this->aggregate(__FUNCTION__, array($column));}/** * Retrieve the maximum value of a given column. * * @param  string  $column * @return mixed */public function max($column){return $this->aggregate(__FUNCTION__, array($column));}/** * Retrieve the sum of the values of a given column. * * @param  string  $column * @return mixed */public function sum($column){return $this->aggregate(__FUNCTION__, array($column));}/** * Retrieve the average of the values of a given column. * * @param  string  $column * @return mixed */public function avg($column){return $this->aggregate(__FUNCTION__, array($column));}/** * Execute an aggregate function on the database. * * @param  string  $function * @param  array   $columns * @return mixed */public function aggregate($function, $columns = array('*')){$this->aggregate = compact('function', 'columns');$results = $this->get($columns);// Once we have executed the query, we will reset the aggregate property so// that more select queries can be executed against the database without// the aggregate value getting in the way when the grammar builds it.$this->columns = null; $this->aggregate = null;if (isset($results[0])){$result = (array) $results[0];return $result['aggregate'];}}/** * Insert a new record into the database. * * @param  array  $values * @return bool */public function insert(array $values){// Since every insert gets treated like a batch insert, we will make sure the// bindings are structured in a way that is convenient for building these// inserts statements by verifying the elements are actually an array.if ( ! is_array(reset($values))){$values = array($values);}// Since every insert gets treated like a batch insert, we will make sure the// bindings are structured in a way that is convenient for building these// inserts statements by verifying the elements are actually an array.else{foreach ($values as $key => $value){ksort($value); $values[$key] = $value;}}// We'll treat every insert like a batch insert so we can easily insert each// of the records into the database consistently. This will make it much// easier on the grammars to just handle one type of record insertion.$bindings = array();foreach ($values as $record){$bindings = array_merge($bindings, array_values($record));}$sql = $this->grammar->compileInsert($this, $values);// Once we have compiled the insert statement's SQL we can execute it on the// connection and return a result as a boolean success indicator as that// is the same type of result returned by the raw connection instance.$bindings = $this->cleanBindings($bindings);return $this->connection->insert($sql, $bindings);}/** * Insert a new record and get the value of the primary key. * * @param  array   $values * @param  string  $sequence * @return int */public function insertGetId(array $values, $sequence = null){$sql = $this->grammar->compileInsertGetId($this, $values, $sequence);$values = $this->cleanBindings($values);return $this->processor->processInsertGetId($this, $sql, $values, $sequence);}/** * Update a record in the database. * * @param  array  $values * @return int */public function update(array $values){$bindings = array_values(array_merge($values, $this->bindings));$sql = $this->grammar->compileUpdate($this, $values);return $this->connection->update($sql, $this->cleanBindings($bindings));}/** * 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 = array()){$wrapped = $this->grammar->wrap($column);$columns = array_merge(array($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 = array()){$wrapped = $this->grammar->wrap($column);$columns = array_merge(array($column => $this->raw("$wrapped - $amount")), $extra);return $this->update($columns);}/** * Delete a record from the database. * * @param  mixed  $id * @return int */public function delete($id = null){// If an ID is passed to the method, we will set the where clause to check// the ID to allow developers to simply and quickly remove a single row// from their database without manually specifying the where clauses.if ( ! is_null($id)) $this->where('id', '=', $id);$sql = $this->grammar->compileDelete($this);return $this->connection->delete($sql, $this->bindings);}/** * Run a truncate statement on the table. * * @return void */public function truncate(){foreach ($this->grammar->compileTruncate($this) as $sql => $bindings){$this->connection->statement($sql, $bindings);}}/** * Get a new instance of the query builder. * * @return \Illuminate\Database\Query\Builder */public function newQuery(){return new Builder($this->connection, $this->grammar, $this->processor);}/** * Merge an array of where clauses and bindings. * * @param  array  $wheres * @param  array  $bindings * @return void */public function mergeWheres($wheres, $bindings){$this->wheres = array_merge($this->wheres, (array) $wheres);$this->bindings = array_values(array_merge($this->bindings, (array) $bindings));}/** * Remove all of the expressions from a list of bindings. * * @param  array  $bindings * @return array */protected function cleanBindings(array $bindings){return array_values(array_filter($bindings, function($binding){return ! $binding instanceof Expression;}));}/** * Create a raw database expression. * * @param  mixed  $value * @return \Illuminate\Database\Query\Expression */public function raw($value){return $this->connection->raw($value);}/** * Get the current query value bindings. * * @return array */public function getBindings(){return $this->bindings;}/** * Set the bindings on the query builder. * * @param  array  $bindings * @return \Illuminate\Database\Query\Builder */public function setBindings(array $bindings){$this->bindings = $bindings;return $this;}/** * Merge an array of bindings into our bindings. * * @param  \Illuminate\Database\Query\Builder  $query * @return \Illuminate\Database\Query\Builder */public function mergeBindings(Builder $query){$this->bindings = array_values(array_merge($this->bindings, $query->bindings));return $this;}/** * Get the database connection instance. * * @return \Illuminate\Database\ConnectionInterface */public function getConnection(){return $this->connection;}/** * Get the database query processor instance. * * @return \Illuminate\Database\Query\Processors\Processor */public function getProcessor(){return $this->processor;}/** * Get the query grammar instance. * * @return \Illuminate\Database\Grammar */public function getGrammar(){return $this->grammar;}/** * Handle dynamic method calls into the method. * * @param  string  $method * @param  array   $parameters * @return mixed */public function __call($method, $parameters){if (starts_with($method, 'where')){return $this->dynamicWhere($method, $parameters);}$className = get_class($this);throw new \BadMethodCallException("Call to undefined method {$className}::{$method}()");}}</span>


Illuminate\Database\Eloquent\Builder
这个Builder类,定义了所有数据查询的结果集

class Builder {/** * The base query builder instance. * * @var \Illuminate\Database\Query\Builder */protected $query;/** * The model being queried. * * @var \Illuminate\Database\Eloquent\Model */protected $model;/** * The relationships that should be eager loaded. * * @var array */protected $eagerLoad = array();/** * The methods that should be returned from query builder. * * @var array */protected $passthru = array('toSql', 'lists', 'insert', 'insertGetId', 'pluck','count', 'min', 'max', 'avg', 'sum', 'exists',);/** * Create a new Eloquent query builder instance. * * @param  \Illuminate\Database\Query\Builder  $query * @return void */public function __construct(QueryBuilder $query){$this->query = $query;}/** * Find a model by its primary key. * * @param  mixed  $id * @param  array  $columns * @return \Illuminate\Database\Eloquent\Model|static|null */public function find($id, $columns = array('*')){if (is_array($id)){    return $this->findMany($id, $columns);}$this->query->where($this->model->getKeyName(), '=', $id);return $this->first($columns);}/** * Find a model by its primary key. * * @param  array  $id * @param  array  $columns * @return \Illuminate\Database\Eloquent\Model|Collection|static */public function findMany($id, $columns = array('*')){$this->query->whereIn($this->model->getKeyName(), $id);return $this->get($columns);    }/** * Find a model by its primary key or throw an exception. * * @param  mixed  $id * @param  array  $columns * @return \Illuminate\Database\Eloquent\Model|static */public function findOrFail($id, $columns = array('*')){if ( ! is_null($model = $this->find($id, $columns))) return $model;throw new ModelNotFoundException;}/** * Execute the query and get the first result. * * @param  array  $columns * @return \Illuminate\Database\Eloquent\Model|static|null */public function first($columns = array('*')){return $this->take(1)->get($columns)->first();}/** * Execute the query and get the first result or throw an exception. * * @param  array  $columns * @return \Illuminate\Database\Eloquent\Model|static */public function firstOrFail($columns = array('*')){if ( ! is_null($model = $this->first($columns))) return $model;throw new ModelNotFoundException;}/** * Execute the query as a "select" statement. * * @param  array  $columns * @return \Illuminate\Database\Eloquent\Collection|static[] */public function get($columns = array('*')){$models = $this->getModels($columns);// If we actually found models we will also eager load any relationships that// have been specified as needing to be eager loaded, which will solve the// n+1 query issue for the developers to avoid running a lot of queries.if (count($models) > 0){$models = $this->eagerLoadRelations($models);}return $this->model->newCollection($models);}/** * Pluck a single column from the database. * * @param  string  $column * @return mixed */public function pluck($column){$result = $this->first(array($column));if ($result) return $result->{$column};}/** * Chunk the results of the query. * * @param  int  $count * @param  callable  $callback * @return void */public function chunk($count, $callback){$results = $this->forPage($page = 1, $count)->get();while (count($results) > 0){// On each chunk result set, we will pass them to the callback and then let the// developer take care of everything within the callback, which allows us to// keep the memory low for spinning through large result sets for working.call_user_func($callback, $results);$page++;$results = $this->forPage($page, $count)->get();}}/** * Get an array with the values of a given column. * * @param  string  $column * @param  string  $key * @return array */public function lists($column, $key = null){$results = $this->query->lists($column, $key);// If the model has a mutator for the requested column, we will spin through// the results and mutate the values so that the mutated version of these// columns are returned as you would expect from these Eloquent models.if ($this->model->hasGetMutator($column)){foreach ($results as $key => &$value){$fill = array($column => $value);$value = $this->model->newFromBuilder($fill)->$column;}}return $results;}/** * Get a paginator for the "select" statement. * * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */public function paginate($perPage = null, $columns = array('*')){$perPage = $perPage ?: $this->model->getPerPage();$paginator = $this->query->getConnection()->getPaginator();if (isset($this->query->groups)){return $this->groupedPaginate($paginator, $perPage, $columns);}else{return $this->ungroupedPaginate($paginator, $perPage, $columns);}}/** * Get a paginator for a grouped statement. * * @param  \Illuminate\Pagination\Environment  $paginator * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */protected function groupedPaginate($paginator, $perPage, $columns){$results = $this->get($columns)->all();return $this->query->buildRawPaginator($paginator, $results, $perPage);}/** * Get a paginator for an ungrouped statement. * * @param  \Illuminate\Pagination\Environment  $paginator * @param  int    $perPage * @param  array  $columns * @return \Illuminate\Pagination\Paginator */protected function ungroupedPaginate($paginator, $perPage, $columns){$total = $this->query->getPaginationCount();// Once we have the paginator we need to set the limit and offset values for// the query so we can get the properly paginated items. Once we have an// array of items we can create the paginator instances for the items.$page = $paginator->getCurrentPage($total);$this->query->forPage($page, $perPage);return $paginator->make($this->get($columns)->all(), $total, $perPage);}/** * Update a record in the database. * * @param  array  $values * @return int */public function update(array $values){return $this->query->update($this->addUpdatedAtColumn($values));}/** * 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 = array()){$extra = $this->addUpdatedAtColumn($extra);return $this->query->increment($column, $amount, $extra);}/** * 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 = array()){$extra = $this->addUpdatedAtColumn($extra);return $this->query->decrement($column, $amount, $extra);}/** * Add the "updated at" column to an array of values. * * @param  array  $values * @return array */protected function addUpdatedAtColumn(array $values){if ( ! $this->model->usesTimestamps()) return $values;$column = $this->model->getUpdatedAtColumn();return array_add($values, $column, $this->model->freshTimestampString());}/** * Delete a record from the database. * * @return int */public function delete(){if ($this->model->isSoftDeleting()){return $this->softDelete();}else{return $this->query->delete();}}/** * Soft delete the record in the database. * * @return int */protected function softDelete(){$column = $this->model->getDeletedAtColumn();return $this->update(array($column => $this->model->freshTimestampString()));}/** * Force a delete on a set of soft deleted models. * * @return int */public function forceDelete(){return $this->query->delete();}/** * Restore the soft-deleted model instances. * * @return int */public function restore(){if ($this->model->isSoftDeleting()){$column = $this->model->getDeletedAtColumn();return $this->update(array($column => null));}}/** * Include the soft deleted models in the results. * * @return \Illuminate\Database\Eloquent\Builder|static */public function withTrashed(){$column = $this->model->getQualifiedDeletedAtColumn();foreach ((array) $this->query->wheres as $key => $where){// If the where clause is a soft delete date constraint, we will remove it from// the query and reset the keys on the wheres. This allows this developer to// include deleted model in a relationship result set that is lazy loaded.if ($this->isSoftDeleteConstraint($where, $column)){unset($this->query->wheres[$key]);$this->query->wheres = array_values($this->query->wheres);}}return $this;}/** * Force the result set to only included soft deletes. * * @return \Illuminate\Database\Eloquent\Builder|static */public function onlyTrashed(){$this->withTrashed();$this->query->whereNotNull($this->model->getQualifiedDeletedAtColumn());return $this;}/** * Determine if the given where clause is a soft delete constraint. * * @param  array   $where * @param  string  $column * @return bool */protected function isSoftDeleteConstraint(array $where, $column){return $where['column'] == $column and $where['type'] == 'Null';}/** * Get the hydrated models without eager loading. * * @param  array  $columns * @return array|static[] */public function getModels($columns = array('*')){// First, we will simply get the raw results from the query builders which we// can use to populate an array with Eloquent models. We will pass columns// that should be selected as well, which are typically just everything.$results = $this->query->get($columns);$connection = $this->model->getConnectionName();$models = array();// Once we have the results, we can spin through them and instantiate a fresh// model instance for each records we retrieved from the database. We will// also set the proper connection name for the model after we create it.foreach ($results as $result){$models[] = $model = $this->model->newFromBuilder($result);$model->setConnection($connection);}return $models;}/** * Eager load the relationships for the models. * * @param  array  $models * @return array */public function eagerLoadRelations(array $models){foreach ($this->eagerLoad as $name => $constraints){// For nested eager loads we'll skip loading them here and they will be set as an// eager load on the query to retrieve the relation so that they will be eager// loaded on that query, because that is where they get hydrated as models.if (strpos($name, '.') === false){$models = $this->loadRelation($models, $name, $constraints);}}return $models;}/** * Eagerly load the relationship on a set of models. * * @param  array     $models * @param  string    $name * @param  \Closure  $constraints * @return array */protected function loadRelation(array $models, $name, Closure $constraints){// First we will "back up" the existing where conditions on the query so we can// add our eager constraints. Then we will merge the wheres that were on the// query back to it in order that any where conditions might be specified.$relation = $this->getRelation($name);$relation->addEagerConstraints($models);call_user_func($constraints, $relation);$models = $relation->initRelation($models, $name);// Once we have the results, we just match those back up to their parent models// using the relationship instance. Then we just return the finished arrays// of models which have been eagerly hydrated and are readied for return.$results = $relation->get();return $relation->match($models, $results, $name);}/** * Get the relation instance for the given relation name. * * @param  string  $relation * @return \Illuminate\Database\Eloquent\Relations\Relation */public function getRelation($relation){$me = $this;// We want to run a relationship query without any constrains so that we will// not have to remove these where clauses manually which gets really hacky// and is error prone while we remove the developer's own where clauses.$query = Relation::noConstraints(function() use ($me, $relation){return $me->getModel()->$relation();});$nested = $this->nestedRelations($relation);// If there are nested relationships set on the query, we will put those onto// the query instances so that they can be handled after this relationship// is loaded. In this way they will all trickle down as they are loaded.if (count($nested) > 0){$query->getQuery()->with($nested);}return $query;}/** * Get the deeply nested relations for a given top-level relation. * * @param  string  $relation * @return array */protected function nestedRelations($relation){$nested = array();// We are basically looking for any relationships that are nested deeper than// the given top-level relationship. We will just check for any relations// that start with the given top relations and adds them to our arrays.foreach ($this->eagerLoad as $name => $constraints){if ($this->isNested($name, $relation)){$nested[substr($name, strlen($relation.'.'))] = $constraints;}}return $nested;}/** * Determine if the relationship is nested. * * @param  string  $name * @param  string  $relation * @return bool */protected function isNested($name, $relation){$dots = str_contains($name, '.');return $dots and starts_with($name, $relation) and $name != $relation;}/** * Add a relationship count condition to the query. * * @param  string  $relation * @param  string  $operator * @param  int     $count * @param  string  $boolean * @return \Illuminate\Database\Eloquent\Builder|static */public function has($relation, $operator = '>=', $count = 1, $boolean = 'and'){$instance = $this->model->$relation();$query = $instance->getRelationCountQuery($instance->getRelated()->newQuery());$this->query->mergeBindings($query->getQuery());return $this->where(new Expression('('.$query->toSql().')'), $operator, $count, $boolean);}/** * Add a relationship count condition to the query with an "or". * * @param  string  $relation * @param  string  $operator * @param  int     $count * @return \Illuminate\Database\Eloquent\Builder|static */public function orHas($relation, $operator = '>=', $count = 1){return $this->has($relation, $operator, $count, 'or');}/** * Set the relationships that should be eager loaded. * * @param  dynamic  $relations * @return \Illuminate\Database\Eloquent\Builder|static */public function with($relations){if (is_string($relations)) $relations = func_get_args();$eagers = $this->parseRelations($relations);$this->eagerLoad = array_merge($this->eagerLoad, $eagers);return $this;}/** * Parse a list of relations into individuals. * * @param  array  $relations * @return array */protected function parseRelations(array $relations){$results = array();foreach ($relations as $name => $constraints){// If the "relation" value is actually a numeric key, we can assume that no// constraints have been specified for the eager load and we'll just put// an empty Closure with the loader so that we can treat all the same.if (is_numeric($name)){$f = function() {};list($name, $constraints) = array($constraints, $f);}// We need to separate out any nested includes. Which allows the developers// to load deep relationships using "dots" without stating each level of// the relationship with its own key in the array of eager load names.$results = $this->parseNested($name, $results);$results[$name] = $constraints;}return $results;}/** * Parse the nested relationships in a relation. * * @param  string  $name * @param  array   $results * @return array */protected function parseNested($name, $results){$progress = array();// If the relation has already been set on the result array, we will not set it// again, since that would override any constraints that were already placed// on the relationships. We will only set the ones that are not specified.foreach (explode('.', $name) as $segment){$progress[] = $segment;if ( ! isset($results[$last = implode('.', $progress)])){ $results[$last] = function() {}; }}return $results;}/** * Call the given model scope on the underlying model. * * @param  string  $scope * @param  array  $parameters * @return \Illuminate\Database\Query\Builder */protected function callScope($scope, $parameters){array_unshift($parameters, $this);return call_user_func_array(array($this->model, $scope), $parameters) ?: $this;}/** * Get the underlying query builder instance. * * @return \Illuminate\Database\Query\Builder|static */public function getQuery(){return $this->query;}/** * Set the underlying query builder instance. * * @param  \Illuminate\Database\Query\Builder  $query * @return void */public function setQuery($query){$this->query = $query;}/** * Get the relationships being eagerly loaded. * * @return array */public function getEagerLoads(){return $this->eagerLoad;}/** * Set the relationships being eagerly loaded. * * @param  array  $eagerLoad * @return void */public function setEagerLoads(array $eagerLoad){$this->eagerLoad = $eagerLoad;}/** * Get the model instance being queried. * * @return \Illuminate\Database\Eloquent\Model */public function getModel(){return $this->model;}/** * Set a model instance for the model being queried. * * @param  \Illuminate\Database\Eloquent\Model  $model * @return \Illuminate\Database\Eloquent\Builder */public function setModel(Model $model){$this->model = $model;$this->query->from($model->getTable());return $this;}/** * Dynamically handle calls into the query instance. * * @param  string  $method * @param  array   $parameters * @return mixed */public function __call($method, $parameters){if (method_exists($this->model, $scope = 'scope'.ucfirst($method))){return $this->callScope($scope, $parameters);}else{$result = call_user_func_array(array($this->query, $method), $parameters);}return in_array($method, $this->passthru) ? $result : $this;}/** * Force a clone of the underlying query builder when cloning. * * @return void */public function __clone(){$this->query = clone $this->query;}}


Eloquent
0 0