query.php 14.3 KB
Newer Older
1
<?php namespace Laravel\Database; use Laravel\Paginator;
2 3 4 5

class Query {

	/**
Taylor Otwell committed
6
	 * The database connection.
7
	 *
8
	 * @var Connection
9
	 */
Taylor Otwell committed
10
	public $connection;
11 12

	/**
13
	 * The query grammar instance.
14
	 *
15
	 * @var Grammars\Grammar
16
	 */
17
	public $grammar;
18 19

	/**
20 21
	 * The SELECT clause.
	 *
Taylor Otwell committed
22
	 * @var array
23
	 */
Taylor Otwell committed
24
	public $selects;
25 26

	/**
27
	 * The aggregating column and function.
28
	 *
Taylor Otwell committed
29
	 * @var array
30
	 */
Taylor Otwell committed
31
	public $aggregate;
32 33

	/**
Taylor Otwell committed
34
	 * Indicates if the query should return distinct results.
35
	 *
Taylor Otwell committed
36
	 * @var bool
37
	 */
Taylor Otwell committed
38
	public $distinct = false;
39 40 41 42 43 44

	/**
	 * The table name.
	 *
	 * @var string
	 */
Taylor Otwell committed
45
	public $from;
46 47

	/**
Taylor Otwell committed
48
	 * The table joins.
49
	 *
Taylor Otwell committed
50
	 * @var array
51
	 */
Taylor Otwell committed
52
	public $joins;
53 54

	/**
Taylor Otwell committed
55
	 * The WHERE clauses.
56
	 *
Taylor Otwell committed
57
	 * @var array
58
	 */
Taylor Otwell committed
59
	public $wheres;
60 61

	/**
Taylor Otwell committed
62
	 * The ORDER BY clauses.
63 64 65
	 *
	 * @var array
	 */
Taylor Otwell committed
66
	public $orderings;
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

	/**
	 * The LIMIT value.
	 *
	 * @var int
	 */
	public $limit;

	/**
	 * The OFFSET value.
	 *
	 * @var int
	 */
	public $offset;

	/**
	 * The query value bindings.
	 *
	 * @var array
	 */
	public $bindings = array();

	/**
	 * Create a new query instance.
	 *
Taylor Otwell committed
92 93 94
	 * @param  Connection        $connection
	 * @param  Grammars\Grammar  $grammar
	 * @param  string            $table
95 96
	 * @return void
	 */
97
	public function __construct(Connection $connection, Grammars\Grammar $grammar, $table)
98
	{
Taylor Otwell committed
99
		$this->from = $table;
100
		$this->grammar = $grammar;
101
		$this->connection = $connection;
102 103 104 105 106 107 108 109 110 111 112 113 114 115
	}

	/**
	 * Force the query to return distinct results.
	 *
	 * @return Query
	 */
	public function distinct()
	{
		$this->distinct = true;
		return $this;
	}

	/**
Taylor Otwell committed
116 117
	 * Add an array of columns to the SELECT clause.
	 *
118
	 * @param  array  $columns
119 120
	 * @return Query
	 */
121
	public function select($columns = array('*'))
122
	{
Taylor Otwell committed
123
		$this->selects = (array) $columns;
124 125 126 127
		return $this;
	}

	/**
Taylor Otwell committed
128
	 * Add a join clause to the query.
129
	 *
130 131 132 133 134 135 136 137 138
	 * @param  string  $table
	 * @param  string  $column1
	 * @param  string  $operator
	 * @param  string  $column2
	 * @param  string  $type
	 * @return Query
	 */
	public function join($table, $column1, $operator, $column2, $type = 'INNER')
	{
Taylor Otwell committed
139
		$this->joins[] = compact('type', 'table', 'column1', 'operator', 'column2');
Taylor Otwell committed
140

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
		return $this;
	}

	/**
	 * Add a left join to the query.
	 *
	 * @param  string  $table
	 * @param  string  $column1
	 * @param  string  $operator
	 * @param  string  $column2
	 * @return Query
	 */
	public function left_join($table, $column1, $operator, $column2)
	{
		return $this->join($table, $column1, $operator, $column2, 'LEFT');
	}

	/**
Taylor Otwell committed
159
	 * Reset the where clause to its initial state. All bindings will be cleared.
Taylor Otwell committed
160 161 162 163 164
	 *
	 * @return void
	 */
	public function reset_where()
	{
165
		list($this->wheres, $this->bindings) = array(array(), array());
Taylor Otwell committed
166 167 168
	}

	/**
169 170 171 172 173 174 175 176 177
	 * Add a raw where condition to the query.
	 *
	 * @param  string  $where
	 * @param  array   $bindings
	 * @param  string  $connector
	 * @return Query
	 */
	public function raw_where($where, $bindings = array(), $connector = 'AND')
	{
178
		$this->wheres[] = array('type' => 'where_raw', 'connector' => $connector, 'sql' => $where);
Taylor Otwell committed
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
		$this->bindings = array_merge($this->bindings, $bindings);

		return $this;
	}

	/**
	 * Add a raw or where condition to the query.
	 *
	 * @param  string  $where
	 * @param  array   $bindings
	 * @return Query
	 */
	public function raw_or_where($where, $bindings = array())
	{
		return $this->raw_where($where, $bindings, 'OR');
	}

	/**
	 * Add a where condition to the query.
	 *
	 * @param  string  $column
	 * @param  string  $operator
	 * @param  mixed   $value
	 * @param  string  $connector
	 * @return Query
	 */
	public function where($column, $operator, $value, $connector = 'AND')
	{
208 209 210
		$type = 'where';

		$this->wheres[] = compact('type', 'column', 'operator', 'value', 'connector');
Taylor Otwell committed
211

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
		$this->bindings[] = $value;

		return $this;
	}

	/**
	 * Add an or where condition to the query.
	 *
	 * @param  string  $column
	 * @param  string  $operator
	 * @param  mixed   $value
	 * @return Query
	 */
	public function or_where($column, $operator, $value)
	{
		return $this->where($column, $operator, $value, 'OR');
	}

	/**
231
	 * Add an or where condition for the primary key to the query.
Taylor Otwell committed
232
	 *
233 234 235 236 237 238 239 240 241
	 * @param  mixed  $value
	 * @return Query
	 */
	public function or_where_id($value)
	{
		return $this->or_where('id', '=', $value);		
	}

	/**
242 243 244 245 246
	 * Add a where in condition to the query.
	 *
	 * @param  string  $column
	 * @param  array   $values
	 * @param  string  $connector
Taylor Otwell committed
247
	 * @param  bool    $not
248 249
	 * @return Query
	 */
Taylor Otwell committed
250
	public function where_in($column, $values, $connector = 'AND', $not = false)
251
	{
252 253 254
		$type = ($not) ? 'where_not_in' : 'where_in';

		$this->wheres[] = compact('type', 'column', 'values', 'connector');
Taylor Otwell committed
255

256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
		$this->bindings = array_merge($this->bindings, $values);

		return $this;
	}

	/**
	 * Add an or where in condition to the query.
	 *
	 * @param  string  $column
	 * @param  array   $values
	 * @return Query
	 */
	public function or_where_in($column, $values)
	{
		return $this->where_in($column, $values, 'OR');
	}

	/**
	 * Add a where not in condition to the query.
	 *
	 * @param  string  $column
	 * @param  array   $values
	 * @param  string  $connector
	 * @return Query
	 */
	public function where_not_in($column, $values, $connector = 'AND')
	{
Taylor Otwell committed
283
		return $this->where_in($column, $values, $connector, true);
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
	}

	/**
	 * Add an or where not in condition to the query.
	 *
	 * @param  string  $column
	 * @param  array   $values
	 * @return Query
	 */
	public function or_where_not_in($column, $values)
	{
		return $this->where_not_in($column, $values, 'OR');
	}

	/**
	 * Add a where null condition to the query.
	 *
	 * @param  string  $column
	 * @param  string  $connector
Taylor Otwell committed
303
	 * @param  bool    $not
304 305
	 * @return Query
	 */
Taylor Otwell committed
306
	public function where_null($column, $connector = 'AND', $not = false)
307
	{
308 309 310
		$type = ($not) ? 'where_not_null' : 'where_null';

		$this->wheres[] = compact('type', 'column', 'connector');
Taylor Otwell committed
311

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
		return $this;
	}

	/**
	 * Add an or where null condition to the query.
	 *
	 * @param  string  $column
	 * @return Query
	 */
	public function or_where_null($column)
	{
		return $this->where_null($column, 'OR');
	}

	/**
	 * Add a where not null condition to the query.
	 *
	 * @param  string  $column
	 * @param  string  $connector
	 * @return Query
	 */
	public function where_not_null($column, $connector = 'AND')
	{
Taylor Otwell committed
335
		return $this->where_null($column, $connector, true);
336 337 338 339 340 341 342 343 344 345 346 347 348 349
	}

	/**
	 * Add an or where not null condition to the query.
	 *
	 * @param  string  $column
	 * @return Query
	 */
	public function or_where_not_null($column)
	{
		return $this->where_not_null($column, 'OR');
	}

	/**
Taylor Otwell committed
350 351
	 * Add dynamic where conditions to the query.
	 *
Taylor Otwell committed
352 353 354
	 * Dynamic queries are caught by the __call magic method and are parsed here.
	 * They provide a convenient, expressive API for building simple conditions.
	 *
Taylor Otwell committed
355 356 357 358 359 360 361 362 363 364 365 366
	 * @param  string  $method
	 * @param  array   $parameters
	 * @return Query
	 */
	private function dynamic_where($method, $parameters)
	{
		// Strip the "where_" off of the method.
		$finder = substr($method, 6);

		// Split the column names from the connectors.
		$segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);

367 368 369
		// The connector variable will determine which connector will be
		// used for the condition. We'll change it as we come across new
		// connectors in the dynamic method string.
Taylor Otwell committed
370
		//
371 372 373
		// The index variable helps us get the correct parameter value
		// for the where condition. We increment it each time we add
		// a condition to the query.
Taylor Otwell committed
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
		$connector = 'AND';

		$index = 0;

		foreach ($segments as $segment)
		{
			if ($segment != '_and_' and $segment != '_or_')
			{
				$this->where($segment, '=', $parameters[$index], $connector);

				$index++;
			}
			else
			{
				$connector = trim(strtoupper($segment), '_');
			}
		}

		return $this;
	}

	/**
396 397 398 399 400 401
	 * Add an ordering to the query.
	 *
	 * @param  string  $column
	 * @param  string  $direction
	 * @return Query
	 */
402
	public function order_by($column, $direction = 'asc')
403
	{
Taylor Otwell committed
404
		$this->orderings[] = compact('column', 'direction');
405 406 407 408 409 410 411 412 413 414 415
		return $this;
	}

	/**
	 * Set the query offset.
	 *
	 * @param  int  $value
	 * @return Query
	 */
	public function skip($value)
	{
Taylor Otwell committed
416
		$this->offset = $value;
417 418 419 420 421 422 423 424 425 426 427
		return $this;
	}

	/**
	 * Set the query limit.
	 *
	 * @param  int  $value
	 * @return Query
	 */
	public function take($value)
	{
Taylor Otwell committed
428
		$this->limit = $value;
429 430 431 432
		return $this;
	}

	/**
433 434 435 436 437 438
	 * Set the query limit and offset for a given page and item per page count.
	 *
	 * @param  int    $page
	 * @param  int    $per_page
	 * @return Query
	 */
Taylor Otwell committed
439
	public function for_page($page, $per_page)
440
	{
Taylor Otwell committed
441
		return $this->skip(($page - 1) * $per_page)->take($per_page);
442 443 444
	}

	/**
Taylor Otwell committed
445
	 * Find a record by the primary key.
446
	 *
Taylor Otwell committed
447
	 * @param  int     $id
448
	 * @param  array   $columns
449 450
	 * @return object
	 */
Taylor Otwell committed
451
	public function find($id, $columns = array('*'))
452
	{
Taylor Otwell committed
453
		return $this->where('id', '=', $id)->first($columns);
454 455 456
	}

	/**
457
	 * Execute the query as a SELECT statement and return a single column.
458 459 460 461
	 *
	 * @param  string  $column
	 * @return mixed
	 */
462
	public function only($column)
463
	{
464
		$this->select(array($column));
Taylor Otwell committed
465

466
		return $this->connection->only($this->grammar->select($this), $this->bindings);
467 468 469
	}

	/**
Taylor Otwell committed
470
	 * Execute the query as a SELECT statement and return the first result.
471
	 *
472 473 474 475
	 * If a single column is selected from the database, only the value of that column will be returned.
	 *
	 * @param  array  $columns
	 * @return mixed
476
	 */
Taylor Otwell committed
477
	public function first($columns = array('*'))
478
	{
479 480
		$columns = (array) $columns;

481
		return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null;
Taylor Otwell committed
482 483 484 485 486 487 488 489 490 491
	}

	/**
	 * Execute the query as a SELECT statement.
	 *
	 * @param  array  $columns
	 * @return array
	 */
	public function get($columns = array('*'))
	{
Taylor Otwell committed
492
		if (is_null($this->selects)) $this->select($columns);
Taylor Otwell committed
493

494
		$results = $this->connection->query($this->grammar->select($this), $this->bindings);
495

496 497 498
		// Reset the SELECT clause so more queries can be performed using
		// the same instance. This is helpful for getting aggregates and
		// then getting actual results.
Taylor Otwell committed
499
		$this->selects = null;
500 501 502 503 504

		return $results;
	}

	/**
505 506 507 508 509 510 511 512 513 514 515 516
	 * Get an aggregate value.
	 *
	 * @param  string  $aggregate
	 * @param  string  $column
	 * @return mixed
	 */
	private function aggregate($aggregator, $column)
	{
		$this->aggregate = compact('aggregator', 'column');

		$result = $this->connection->only($this->grammar->select($this), $this->bindings);

517 518 519
		// Reset the aggregate so more queries can be performed using
		// the same instance. This is helpful for getting aggregates
		// and then getting actual results.
520 521 522 523 524 525
		$this->aggregate = null;

		return $result;
	}

	/**
526 527 528 529 530 531
	 * Get the paginated query results as a Paginator instance.
	 *
	 * @param  int        $per_page
	 * @param  array      $columns
	 * @return Paginator
	 */
532
	public function paginate($per_page = 20, $columns = array('*'))
533
	{
534 535 536 537 538 539
		// Because some database engines may throw errors if we leave
		// orderings on the query when retrieving the total number
		// of records, we will remove all of the ordreings and put
		// them back on the query after we have the count.
		list($orderings, $this->orderings) = array($this->orderings, null);

540 541
		$page = Paginator::page($total = $this->count(), $per_page);

542 543
		$this->orderings = $orderings;

544 545 546 547
		return Paginator::make($this->for_page($page, $per_page)->get($columns), $total, $per_page);
	}

	/**
Taylor Otwell committed
548 549
	 * Insert an array of values into the database table.
	 *
550 551 552 553 554
	 * @param  array  $values
	 * @return bool
	 */
	public function insert($values)
	{
555 556 557
		// Force every insert to be treated like a batch insert to make creating
		// the binding array simpler since we can just spin through the inserted
		// rows as if there/ was more than one every time.
558 559 560 561 562 563 564 565 566 567
		if ( ! is_array(reset($values))) $values = array($values);

		$bindings = array();

		foreach ($values as $value)
		{
			$bindings = array_merge($bindings, array_values($value));
		}

		return $this->connection->query($this->grammar->insert($this, $values), $bindings);
568 569 570
	}

	/**
571 572
	 * Insert an array of values into the database table and
	 * return the value of the ID column.
Taylor Otwell committed
573
	 *
574 575
	 * @param  array   $values
	 * @param  string  $sequence
576 577
	 * @return int
	 */
578
	public function insert_get_id($values, $sequence = null)
579
	{
580
		$this->connection->query($this->grammar->insert($this, $values), array_values($values));
581

582
		return (int) $this->connection->pdo->lastInsertId($sequence);
Taylor Otwell committed
583 584 585
	}

	/**
586 587 588 589 590 591 592 593
	 * Increment the value of a column by a given amount.
	 *
	 * @param  string  $column
	 * @param  int     $amount
	 * @return int
	 */
	public function increment($column, $amount = 1)
	{
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618
		return $this->adjust($column, $amount, ' + ');
	}

	/**
	 * Decrement the value of a column by a given amount.
	 *
	 * @param  string  $column
	 * @param  int     $amount
	 * @return int
	 */
	public function decrement($column, $amount = 1)
	{
		return $this->adjust($column, $amount, ' - ');
	}

	/**
	 * Adjust the value of a column up or down by a given amount.
	 *
	 * @param  string  $column
	 * @param  int     $amount
	 * @param  string  $operator
	 * @return int
	 */
	protected function adjust($column, $amount, $operator)
	{
619 620 621
		$value = Manager::raw($this->grammar->wrap($column).$operator.$amount);

		return $this->update(array($column => $value));
622 623 624
	}

	/**
Taylor Otwell committed
625 626
	 * Update an array of values in the database table.
	 *
627
	 * @param  array  $values
Taylor Otwell committed
628
	 * @return int
629 630 631
	 */
	public function update($values)
	{
632 633 634
		$bindings =  array_merge(array_values($values), $this->bindings);

		return $this->connection->query($this->grammar->update($this, $values), $bindings);
635 636 637 638 639
	}

	/**
	 * Execute the query as a DELETE statement.
	 *
Taylor Otwell committed
640 641
	 * Optionally, an ID may be passed to the method do delete a specific row.
	 *
642
	 * @param  int   $id
Taylor Otwell committed
643
	 * @return int
644 645 646
	 */
	public function delete($id = null)
	{
647
		if ( ! is_null($id)) $this->where('id', '=', $id);
648

649
		return $this->connection->query($this->grammar->delete($this), $this->bindings);		
650 651 652
	}

	/**
Taylor Otwell committed
653
	 * Magic Method for handling dynamic functions.
Taylor Otwell committed
654
	 *
655 656
	 * This method handles all calls to aggregate functions as well
	 * as the construction of dynamic where clauses.
657 658 659
	 */
	public function __call($method, $parameters)
	{
660 661
		if (strpos($method, 'where_') === 0)
		{
Taylor Otwell committed
662
			return $this->dynamic_where($method, $parameters, $this);
663 664
		}

Taylor Otwell committed
665
		if (in_array($method, array('abs', 'count', 'min', 'max', 'avg', 'sum')))
666
		{
667 668 669 670 671 672 673 674
			if ($method == 'count')
			{
				return $this->aggregate(strtoupper($method), '*');
			}
			else
			{
				return $this->aggregate(strtoupper($method), $parameters[0]);
			}
675
		}
676

Phill Sparks committed
677
		throw new \BadMethodCallException("Method [$method] is not defined on the Query class.");
678 679
	}

Phill Sparks committed
680
}