44
55use Carbon \Carbon ;
66use Carbon \CarbonInterface ;
7- use Illuminate \Database \Connection ;
87use Illuminate \Database \Eloquent \Model ;
98use Illuminate \Database \Eloquent \Relations \BelongsTo ;
109use Illuminate \Support \Facades \Date ;
11- use PDO ;
10+ use Zap \ Models \ Builders \ SchedulePeriodBuilder ;
1211
1312/**
1413 * @property int|string $id
2423 * @property-read int $duration_minutes
2524 * @property-read Carbon $start_date_time
2625 * @property-read Carbon $end_date_time
26+ *
27+ * @method static \Illuminate\Database\Eloquent\Builder available()
28+ * @method static \Illuminate\Database\Eloquent\Builder forDate(string $date)
29+ * @method static \Illuminate\Database\Eloquent\Builder forTimeRange(string $startTime, string $endTime)
30+ * @method static \Illuminate\Database\Eloquent\Builder overlapping(string $date, string $startTime, string $endTime, ?CarbonInterface $endDate = null)
2731 */
2832class SchedulePeriod extends Model
2933{
@@ -73,6 +77,14 @@ public function schedule(): BelongsTo
7377 return $ this ->belongsTo ($ this ->getScheduleClass (), 'schedule_id ' );
7478 }
7579
80+ /**
81+ * Create a new Eloquent query builder for the model.
82+ */
83+ public function newEloquentBuilder ($ query ): SchedulePeriodBuilder
84+ {
85+ return new SchedulePeriodBuilder ($ query );
86+ }
87+
7688 /**
7789 * Get the duration in minutes.
7890 */
@@ -130,60 +142,6 @@ public function isActiveNow(): bool
130142 return $ now ->between ($ startDateTime , $ endDateTime );
131143 }
132144
133- /**
134- * Scope a query to only include available periods.
135- */
136- public function scopeAvailable (\Illuminate \Database \Eloquent \Builder $ query ): \Illuminate \Database \Eloquent \Builder
137- {
138- return $ query ->where ('is_available ' , true );
139- }
140-
141- /**
142- * Scope a query to only include periods for a specific date.
143- */
144- public function scopeForDate (\Illuminate \Database \Eloquent \Builder $ query , string $ date ): \Illuminate \Database \Eloquent \Builder
145- {
146- return $ query ->where ('date ' , Carbon::parse ($ date ));
147- }
148-
149- /**
150- * Scope a query to only include periods within a time range.
151- */
152- public function scopeForTimeRange (\Illuminate \Database \Eloquent \Builder $ query , string $ startTime , string $ endTime ): \Illuminate \Database \Eloquent \Builder
153- {
154- return $ query ->where ('start_time ' , '>= ' , $ startTime )
155- ->where ('end_time ' , '<= ' , $ endTime );
156- }
157-
158- /**
159- * Scope a query to find overlapping periods.
160- */
161- public function scopeOverlapping (\Illuminate \Database \Eloquent \Builder $ query , string $ date , string $ startTime , string $ endTime , ?CarbonInterface $ endDate = null ): \Illuminate \Database \Eloquent \Builder
162- {
163- // Normalize input times to HH:MM format
164- $ startTime = str_pad ($ startTime , 5 , '0 ' , STR_PAD_LEFT );
165- $ endTime = str_pad ($ endTime , 5 , '0 ' , STR_PAD_LEFT );
166-
167- // Apply date filter
168- $ query ->when (is_null ($ endDate ), fn ($ q ) => $ q ->whereDate ('date ' , $ date ));
169-
170- // Apply time overlap logic based on database driver
171-
172- /** @var Connection $connection */
173- $ connection = $ query ->getConnection ();
174- $ driver = $ connection ->getPdo ()->getAttribute (PDO ::ATTR_DRIVER_NAME );
175-
176- if ($ driver === 'sqlite ' ) {
177- return $ this ->applySqliteTimeOverlap ($ query , $ startTime , $ endTime );
178- }
179-
180- if ($ driver === 'pgsql ' ) {
181- return $ this ->applyPostgresTimeOverlap ($ query , $ startTime , $ endTime );
182- }
183-
184- return $ this ->applyStandardTimeOverlap ($ query , $ startTime , $ endTime );
185- }
186-
187145 /**
188146 * Convert the period to a human-readable string.
189147 */
@@ -196,34 +154,4 @@ public function __toString(): string
196154 $ this ->end_time
197155 );
198156 }
199-
200- /**
201- * Apply SQLite-specific time overlap conditions.
202- */
203- private function applySqliteTimeOverlap ($ query , string $ startTime , string $ endTime )
204- {
205- return $ query
206- ->whereRaw ('CASE WHEN LENGTH(start_time) = 4 THEN "0" || start_time ELSE start_time END < ? ' , [$ endTime ])
207- ->whereRaw ('CASE WHEN LENGTH(end_time) = 4 THEN "0" || end_time ELSE end_time END > ? ' , [$ startTime ]);
208- }
209-
210- /**
211- * Apply standard SQL time overlap conditions (MySQL).
212- */
213- private function applyStandardTimeOverlap ($ query , string $ startTime , string $ endTime )
214- {
215- return $ query
216- ->whereRaw ("LPAD(start_time, 5, '0') < ? " , [$ endTime ])
217- ->whereRaw ("LPAD(end_time, 5, '0') > ? " , [$ startTime ]);
218- }
219-
220- /**
221- * Apply PostgreSQL-specific time overlap conditions.
222- */
223- private function applyPostgresTimeOverlap ($ query , string $ startTime , string $ endTime )
224- {
225- return $ query
226- ->whereRaw ('LPAD(start_time::text, 5, \'0 \') < ? ' , [$ endTime ])
227- ->whereRaw ('LPAD(end_time::text, 5, \'0 \') > ? ' , [$ startTime ]);
228- }
229157}
0 commit comments