For this project, we will be creating a simple process for bookings: meaning booking an appointment, a calendar event, or whatever else. And our goal with the timezones is to save/process the booking data according to the user's timezone.
Since this course is not about creating CRUD, I will just quickly run through it as a "setup".
First, we install Laravel with Breeze Starter kit:
laravel new timezonescd timezonescomposer require laravel/breeze --devphp artisan breeze:install blade
Then, we generate the Model/Migration/Controller for bookings:
php artisan make:model Booking -mcrR
And then fill in all the CRUD for listing/creating/editing your own bookings for each user.
Full code is available in (GitHub Repository)[https://github.com/LaravelDaily/laravel-timezones-course]
The main files for Bookings CRUD:
- app/Models/Booking.php - Our main Booking Model
- app/Http/Controllers/BookingController.php - Our main Controller with all CRUD methods
- app/Http/Requests/StoreBookingRequest.php - Store Request
- app/Http/Requests/UpdateBookingRequest.php - Update Request
- resources/views/bookings - Blade Views for index, create, and edit
So you would understand better, here's the Controller code.
app/Http/Controllers/BookingController.php:
use App\Http\Requests\StoreBookingRequest;use App\Http\Requests\UpdateBookingRequest;use App\Models\Booking;use Illuminate\Http\RedirectResponse;use Illuminate\Http\Request; class BookingController extends Controller{ public function index() { $bookings = Booking::query() ->with(['user']) ->get(); return view('bookings.index', compact('bookings')); } public function create() { return view('bookings.create'); } public function store(StoreBookingRequest $request): RedirectResponse { $request->user()->bookings()->create($request->validated()); return redirect()->route('booking.index'); } public function edit(Booking $booking) { return view('bookings.edit', compact('booking')); } public function update(UpdateBookingRequest $request, Booking $booking): RedirectResponse { $booking->update($request->validated()); return redirect()->route('booking.index'); } public function destroy(Request $request, Booking $booking): RedirectResponse { abort_unless($booking->user_id === $request->user()->id, 404); $booking->delete(); return redirect()->route('booking.index'); }}
That's it! This will be our starting point for the whole course. From here, we can add timezone-related functionality.
In Booking migrations, as I check, we are using datetTime data type for start and end column,
Schema::create('bookings', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->dateTime('start'); $table->dateTime('end'); $table->timestamps(); });
why don't we use timestamp data type, from what I know, timestamp data type will also store the offset value from UTC, so it will be easy to convert one timezone to another timezone.This is because we are strictly working with UTC all the time and we want Carbon to handle the offset, not the database itself. If we start storing in users timezone on database level - then we will run into an issue where we will always add the timezone to already timezone specific date. For example:
User input
2020-02-01 03:00:00 - UTC + 3
Database saves2020-02-01 00:00:00 - UTC
But with timestamps we will save
2020-02-01 03:00:00 - UTC + 3
to the database and on display we will add another +3 (hours), making it2020-02-01 06:00:00 - UTC + 3
which is now incorrect :)It's better to always work with UTC timezones on the database level, then do conversions on the system itself, once you know exactly what timezone you need to convert to. Just imagine if you allow 10 users see their booking times - they can't be in local timezone. They must be in their own individual one