This lesson will be about adding a timezone field to the User
Model and Registration process:
Setting Laravel Defaults
Laravel has a default timezone set in config/app.php
:
'timezone' => 'UTC',
We have to ensure it's still set to UTC
as we will be mainly working with UTC dates and times. The idea is that our Database data is always in UTC and we can easily convert it to any other timezone.
Adding Timezone to Users Table
To start with this, we need a new migration:
php artisan make:migration add_timezone_to_users_table
Migration
public function up(): void{ Schema::table('users', function (Blueprint $table) { $table->string('timezone')->default('UTC'); });}
This simply adds a new field called timezone
to our users
table with a default value of UTC
.
Then we need to add this field to our User
Model:
app/Models/User.php
// ... protected $fillable = [ // ... 'timezone',];
Adding Timezone to User Registration
Now that our database supports timezone saving, we need a way to set it. We will do this during registration, and we will use the timezone_identifiers_list() function to get a list of all timezones, and a custom function to attempt to guess user's timezone based on IP address.
Here's the updated Controller of Laravel Breeze.
app/Http/Controllers/Auth/RegisteredUserController.php
class RegisteredUserController extends Controller{ public function create(): View { $timezones = timezone_identifiers_list(); return view('auth.register', compact('timezones')); } public function store(Request $request): RedirectResponse { $request->validate([ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:'.User::class], 'password' => ['required', 'confirmed', Rules\Password::defaults()], 'timezone' => ['required', Rule::in(array_flip(timezone_identifiers_list()))] ]); $user = User::create([ 'name' => $request->name, 'email' => $request->email, 'password' => Hash::make($request->password), 'timezone' => timezone_identifiers_list()[$request->input('timezone', 'UTC')] ]); event(new Registered($user)); Auth::login($user); return redirect(RouteServiceProvider::HOME); }}
With these modifications, we've added:
- List of timezones to the registration view
- Validation for the timezone field
- Saving the timezone to the database
Next, we have to display the field in the registration form:
resources/views/auth/register.blade.php
{{-- ... --}} <!-- Confirm Password --><div class="mt-4"> {{-- ... --}} </div> <!-- Timezone Select --><div class="mt-4"> <x-input-label for="timezone" :value="__('Timezone')"/> <x-select-input id="timezone" class="block mt-1 w-full" name="timezone" :options="$timezones" :selected="old('timezone')" required/> <x-input-error :messages="$errors->get('timezone')" class="mt-2"/></div> {{-- ... --}}
And since we've added the select-input
component, we need to create it.
php artisan make:component SelectInput --view
resources/views/components/select-input.blade.php
@props(['disabled' => false, 'options' => [], 'selected' => null, 'default' => null]) <select {{ $disabled ? 'disabled' : '' }} {!! $attributes->merge(['class' => 'border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-300 focus:border-indigo-500 dark:focus:border-indigo-600 focus:ring-indigo-500 dark:focus:ring-indigo-600 rounded-md shadow-sm']) !!}> <option value="">Select</option> @foreach($options as $key => $value) <option value="{{ $key }}" @selected($selected === $key || $default === $value)>{{ $value }}</option> @endforeach</select>
Fixing Tests
Lastly, we should update our Tests from Laravel Breeze to make sure they still pass:
tests/Feature/Auth/RegistrationTest.php
// ...public function test_new_users_can_register(): void{ $response = $this->post('/register', [ 'name' => 'Test User', 'email' => 'test@example.com', 'password' => 'password', 'password_confirmation' => 'password', // Take the first timezone from the list 'timezone' => array_keys(timezone_identifiers_list())[0] ]); $this->assertAuthenticated(); $response->assertRedirect(RouteServiceProvider::HOME);}// ...
That's it! Loading the registration form we should see that the timezone field is now available:
And it's saved in the database:
Next, we'll add automatic pre-selection of the timezone, you can choose from two options:
- Calling remote 3rd-party API
- Using local JavaScript with no external calls
Automatically Filling Timezone Using 3rd-Party API
To add 3rd party API, we have to modify our User Model:
app/Models/User.php
// ... public static function guessUserTimezoneUsingAPI($ip){ $ip = Http::get('https://ipecho.net/'. $ip .'/json'); if ($ip->json('timezone')) { return $ip->json('timezone'); } return null;}
Then we can use it in our Controller:
app/Http/Controllers/Auth/RegisteredUserController.php
use Illuminate\Http\Request; // ... public function create(Request $request): View{ $timezones = timezone_identifiers_list(); $guessedTimezone = User::guessUserTimezoneUsingAPI($request->ip()); return view('auth.register', compact('timezones', 'guessedTimezone'));} And lastly, we need to modify our View to pre-select the timezone: **resources/views/auth/register.blade.php**```blade<x-select-input id="timezone" class="block mt-1 w-full" name="timezone" :options="$timezones" :selected="old('timezone')" :default="$guessedTimezone" required/>
That's it! Now, when loading the page, we'll see that the timezone is set to our current one:
Code in Repository
Automatically Filling Timezone Using JavaScript
Another option to guess the user's timezone is to use JavaScript INTL system. To do that we'll modify our Blade View:
resources/views/auth/register.blade.php
{{-- ... --}} </form> <script> setSelectedValue(document.getElementById('timezone'), Intl.DateTimeFormat().resolvedOptions().timeZone); function setSelectedValue(selectObj, valueToSet) { for (var i = 0; i < selectObj.options.length; i++) { if (selectObj.options[i].text == valueToSet) { selectObj.options[i].selected = true; return; } } } </script></x-guest-layout>
And that's it! When loading the page, we'll see that the timezone is set to our current one:
We didn't have to use any 3rd party API or a package to do this. Another option is to use Moment JS.
Code in Repository
you seame to be missing somthing Like what to put in the bookings Database Table ! I can find it in the repository but I dont see where you tell us how to set it up.
We are not focusing on the bookings itself here (especially in this lesson). But in any case, if you look at next lesson - you will see the two fields we use and a screenshot from database schema. It was too simple to add here :)
'https://ipecho.net/'. $ip .'/json'
- not available anymore :(