format_relations($relations) ); } /** * Prevent eager loading via withCount() * * @param mixed $relations * @return $this * @throws \RuntimeException */ public function withCount($relations) { // Allow empty withCount() calls if (empty($relations)) { return parent::withCount($relations); } throw new \RuntimeException( 'Eager loading counts via withCount() is not allowed in the RSpade framework. ' . 'Use explicit count queries instead. ' . 'Attempted to eager load counts for: ' . $this->format_relations($relations) ); } /** * Prevent eager loading via withMax() * * @param array|string $relation * @param string $column * @return $this * @throws \RuntimeException */ public function withMax($relation, $column) { // Allow empty withMax() calls if (empty($relation)) { return parent::withMax($relation, $column); } throw new \RuntimeException( 'Eager loading max via withMax() is not allowed in the RSpade framework. ' . 'Use explicit max queries instead.' ); } /** * Prevent eager loading via withMin() * * @param array|string $relation * @param string $column * @return $this * @throws \RuntimeException */ public function withMin($relation, $column) { // Allow empty withMin() calls if (empty($relation)) { return parent::withMin($relation, $column); } throw new \RuntimeException( 'Eager loading min via withMin() is not allowed in the RSpade framework. ' . 'Use explicit min queries instead.' ); } /** * Prevent eager loading via withSum() * * @param array|string $relation * @param string $column * @return $this * @throws \RuntimeException */ public function withSum($relation, $column) { // Allow empty withSum() calls if (empty($relation)) { return parent::withSum($relation, $column); } throw new \RuntimeException( 'Eager loading sum via withSum() is not allowed in the RSpade framework. ' . 'Use explicit sum queries instead.' ); } /** * Prevent eager loading via withAvg() * * @param array|string $relation * @param string $column * @return $this * @throws \RuntimeException */ public function withAvg($relation, $column) { // Allow empty withAvg() calls if (empty($relation)) { return parent::withAvg($relation, $column); } throw new \RuntimeException( 'Eager loading avg via withAvg() is not allowed in the RSpade framework. ' . 'Use explicit avg queries instead.' ); } /** * Prevent eager loading via withExists() * * @param array|string $relation * @return $this * @throws \RuntimeException */ public function withExists($relation) { // Allow empty withExists() calls if (empty($relation)) { return parent::withExists($relation); } throw new \RuntimeException( 'Eager loading exists via withExists() is not allowed in the RSpade framework. ' . 'Use explicit exists queries instead.' ); } /** * Prevent eager loading via withAggregate() * * @param mixed $relations * @param string $column * @param string $function * @return $this * @throws \RuntimeException */ public function withAggregate($relations, $column, $function = null) { // Allow empty withAggregate() calls if (empty($relations)) { return parent::withAggregate($relations, $column, $function); } throw new \RuntimeException( 'Eager loading aggregates via withAggregate() is not allowed in the RSpade framework. ' . 'Use explicit aggregate queries instead.' ); } /** * Prevent lazy eager loading via has() * Note: has() is different - it's for filtering, not loading * But we'll still prevent it if it tries to eager load * * @param string $relation * @param string $operator * @param int $count * @param string $boolean * @param \Closure|null $callback * @return $this */ public function has($relation, $operator = '>=', $count = 1, $boolean = 'and', ?\Closure $callback = null) { // has() is allowed as it's for filtering, not eager loading // But log it for monitoring if (app()->environment() !== 'testing') { logger()->debug('has() query used on relation', [ 'model' => get_class($this->model), 'relation' => $relation, 'trace' => debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 5) ]); } return parent::has($relation, $operator, $count, $boolean, $callback); } /** * Override delete to prevent deleting all records without WHERE clause * * @param mixed $id * @return int * @throws \RuntimeException */ public function delete($id = null) { // If $id is provided, allow it (deleting by primary key) if ($id !== null) { return parent::delete($id); } // Check if there are any WHERE clauses on the underlying query $baseQuery = $this->getQuery(); if (empty($baseQuery->wheres)) { // @PHP-DB-01-EXCEPTION - DB::table() mentioned in error message, not used shouldnt_happen( 'Attempted to delete all records from ' . $this->getModel()->getTable() . ' without WHERE clause. ' . 'This operation is forbidden to prevent accidental data loss. ' . 'If you truly need to delete all records, use DB::table()->truncate() or add a WHERE clause.' ); } return parent::delete(); } /** * Format relations for error messages * * @param mixed $relations * @return string */ protected function format_relations($relations) { if (is_array($relations)) { return implode(', ', array_keys($relations)); } return (string) $relations; } }