
In this part of the series building a simple real estate app we will prepare the database structure for our app and create the migrations and models.
In the previous part we discussed the app overview, and created a laravel project and vue 3 as a frontend template. Also we prepared the template we will be using across the series. Then we created some basic Vue pages and created the routes of all these pages.
Using this knowledge in this article we will discuss and create the database tables that will be used in the real-state app.
At first create a new mysql database using phpmyadmin or mysql cli, then update the .env file settings with database details:
.env
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=realestateapp DB_USERNAME=<database username> DB_PASSWORD=<database password>
Database Tables
Our real estate app will have these tables:
- Features: Store the features required for properties which are so called amenities like water, electricity etc. each feature have an id and a title.
- Properties: Store property details like title, description, area, beds, rooms, user_id, agency_id, etc.
- Property pictures: Store property pictures, as you might guess every property have many pictures.
- Property features: Store property features which are the features stored in the features table related to each property. Each property have many features.
- Users
- Countries: Store countries.
- States: Store country states.
- Cities: Store country cities.
These are the basic tables. For the countries, states and cities tables, these tables used when submitting new property to store the property location. In a real world project you should use some other technique like google maps.
You now have a clear understanding of the database structure, let’s move to create the necessary migrations.
In the project root run these commands:
php artisan make:model Property -m php artisan make:model Feature -m php artisan make:model PropertyFeature -m php artisan make:model PropertyPicture -m php artisan make:model Country -m php artisan make:model State -m php artisan make:model City -m
As you see in the above commands i created the models for each of the above tables using laravel make:model command. The -m option tells laravel to create the migration also along with the model, so each command will create the Model class and the database migration.
For the users table we don’t really need a migration as it’s already exists.
Now update the up() method in every migration file as shown below:
yyyy_mm_dd_create_properties_table.php
public function up() { Schema::create('properties', function (Blueprint $table) { $table->id(); $table->bigInteger('user_id'); $table->string('title'); $table->text('description')->nullable(); $table->enum('status', ["Sale", "Rent"]); $table->decimal('price', 18, 2); $table->integer('rent_amount_per')->nullable(); $table->decimal('area', 18, 2); $table->bigInteger('country'); $table->bigInteger('state'); $table->bigInteger('city')->nullable(); $table->integer('bedrooms')->nullable(); $table->integer('bathrooms')->nullable(); $table->integer('rooms')->nullable(); $table->integer('garages')->nullable(); $table->integer('units')->nullable(); $table->integer('floor_number')->nullable(); $table->integer('year_built')->nullable(); $table->string('property_finalizing')->nullable(); $table->string('phone')->nullable(); $table->string('youtube_video')->nullable(); $table->timestamps(); }); }
yyyy_mm_dd_create_features_table.php
public function up() { Schema::create('features', function (Blueprint $table) { $table->id(); $table->string('title'); $table->timestamps(); }); }
yyyy_mm_dd_create_property_features_table.php
public function up() { Schema::create('property_features', function (Blueprint $table) { $table->id(); $table->bigInteger('property_id'); $table->bigInteger('feature_id'); $table->timestamps(); }); }
yyyy_mm_dd_create_property_pictures_table.php
public function up() { Schema::create('property_pictures', function (Blueprint $table) { $table->id(); $table->bigInteger('property_id'); $table->string('picture'); $table->timestamps(); }); }
yyyy_mm_dd_create_countries_table.php
public function up() { Schema::create('countries', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); }
yyyy_mm_dd_create_states_table.php
public function up() { Schema::create('states', function (Blueprint $table) { $table->id(); $table->string('name'); $table->bigInteger('country_id'); $table->timestamps(); }); }
yyyy_mm_dd_create_cities_table.php
public function up() { Schema::create('cities', function (Blueprint $table) { $table->id(); $table->string('name'); $table->bigInteger('country_id'); $table->string('state_id'); $table->timestamps(); }); }
Also update the create_users_migration add new field “phone” like so:
public function up() { Schema::create('users', function (Blueprint $table) { .... .... $table->string('phone')->nullable(); }); }
After editing the migration files, let’s create the tables by initiating this command:
php artisan migrate
Well, now the tables created.
The next step is to head over and open each model and setup the required eloquent relations like below.
app/Models/Property.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Property extends Model { use HasFactory; public function user() { return $this->belongsTo(User::class, "user_id"); } public function pictures() { return $this->hasMany(PropertyPicture::class, "property_id"); } public function features() { return $this->belongsToMany(Feature::class, 'property_features', 'property_id', 'feature_id'); } public function country() { return $this->belongsTo(Country::class, "country"); } public function state() { return $this->belongsTo(State::class, "state"); } public function city() { return $this->belongsTo(City::class, "city"); } }
app/Models/User.php
<?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { use HasApiTokens, HasFactory, Notifiable; /** * The attributes that are mass assignable. * * @var array<int, string> */ protected $fillable = [ 'name', 'email', 'password', 'phone' ]; /** * The attributes that should be hidden for serialization. * * @var array<int, string> */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast. * * @var array<string, string> */ protected $casts = [ 'email_verified_at' => 'datetime', ]; public function properties() { return $this->hasMany(Property::class, "user_id"); } }
app/Models/Country.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Country extends Model { use HasFactory; public function states() { return $this->hasMany(State::class, "country_id"); } }
app/Models/State.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class State extends Model { use HasFactory; public function cities() { return $this->hasMany(City::class, "state_id"); } public function country() { return $this->belongsTo(Country::class, "state_id"); } }
app/Models/City.php
<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class City extends Model { use HasFactory; public function state() { return $this->belongsTo(State::class, "city_id"); } }
In the above code i have setup the relations we will use in our app. First in the Property model i added some relations like user(), pictures() and features().
In the User model there relation to get the properties for this user.
Let’s proceed to the next part where we will start adding the authentication views and apis.
Continue to part 3: Authentication>>>