Skip to content
Advertisement

How can I create a profile for a user through a form in my application? I am using laravel 8

Hey guys I’m new to Laravel.

I have one user in my database with name, username and password, now I want to create a profile for this user through a form in my application, where the user can submit a profile image, description and url. I also would like the user to be able to edit and submit changes after. How can I do that?

My routes in web.php are:

Route::get('/users/{user}/edit', [UserController::class, 'edit']);

Route::patch('/users/{user}', [UserController::class, 'update']);

My form in edit.blade.php file is as follow:

<form action="/users/{{ $user->id }}" enctype="multipart/form-data" method="post">
              @csrf
              @method('PATCH')

              <div class="form-group">
                  <label for="description" class="edit_description_label">Description</label>

                  <div class="edit_description_div">
                      <input id="description"
                      type="text"
                      class="form-control @error('description') is-invalid @enderror"
                      name="description"
                      value="{{ old('description') ?? $user->profile->description ?? '' }}"
                      autocomplete="description" autofocus>

                      @error('description')
                      <div class="invalid-feedback-div">
                        <span class="invalid-feedback" role="alert">
                            <strong>{{ $message }}</strong>
                        </span>
                      </div>
                      @enderror
                  </div>
              </div>

              <div class="form-group">
                  <label for="url" class="edit_title_label">URL</label>

                  <div class="edit_url_div">
                      <input id="url"
                      type="text"
                      class="form-control @error('url') is-invalid @enderror"
                      name="url" 
                      value="{{ $user->profile->url ?? '' }}"
                      autocomplete="url" autofocus>

                      @error('url')
                      <div class="invalid-feedback-div">
                        <span class="invalid-feedback" role="alert">
                            <strong>{{ $message }}</strong>
                        </span>
                      </div>
                      @enderror
                  </div>
              </div>

              <div class="create_post_image_div">
                <label for="image" class="create_image_label">Profile Image</label>
                <input type="file" class="form-control-file" id="image" name="image">

                @error('image')
                <div class="invalid-feedback-div">
                  <strong>{{ $message }}</strong>
                </div>
                @enderror

                <div class="create_post_btn_div">
                  <button class="create_post_btn">Save Profile</button>
                </div>
              </div>

            </form>

And finally, my methods in UserController are:

public function show(User $user)
{
  return view('user.profile', ['user' => $user]);
}

public function edit(User $user)
{
    return view('user.edit', ['user' => $user]);
}

public function update(Request $request, User $user)
{
    $data = request()->validate([
            'description' => '',
            'url' => '',
            'image' => '',
            ]);

    $user->profile->update($data);

    return redirect('/users/{$user->id}');
}

To also give better context, the profile description, url and image columns are not in my User Model, but rather in my Profile Model. I have defined those relationships, so to allow for better understanding here’s the code for the following files:

This is the code in my User.php file:

<?php

namespace AppModels;

use IlluminateContractsAuthMustVerifyEmail;
use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateFoundationAuthUser as Authenticatable;
use IlluminateNotificationsNotifiable;

class User extends Authenticatable
{
    use HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'username',
        'email',
        'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function profile()
    {
      return $this->hasOne(Profile::class);
    }
}

This is the Users Table file:

<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('username')->unique();
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}

This is the Profile.php file:

<?php

namespace AppModels;

use IlluminateDatabaseEloquentFactoriesHasFactory;
use IlluminateDatabaseEloquentModel;

class Profile extends Model
{
    use HasFactory;

    protected $guarded = [];

    public function user()
    {
      return $this->belongsTo(User::class);
    }
}

And this is the Profiles Table file:

<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

class CreateProfilesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('profiles', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->string('description')->nullable();
            $table->string('url')->nullable();
            $table->string('image')->nullable();
            $table->timestamps();

            $table->index('user_id');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('profiles');
    }
}

Advertisement

Answer

Updates

The save method may also be used to update models that already exist in the database. To update a model, you should retrieve it and set any attributes you wish to update. Then, you should call the model’s save method.

    use AppModelsProfile;
    use IlluminateHttpUploadedFile;
    use IlluminateSupportFacadesStorage;
    use IlluminateHttpRequest;

    public function updateProfile($user)
    {
        $data = request()->validate($this->validationBag());

        $profile = Profile::where('user_id', $user)->first();


        if (isset($data['image'])) {
            $this->updateProfileImage($data['image'], $profile);
        }

        $profile->description = $data['description'];
        $profile->url = $data['url'];
        $profile->save();

        return redirect("/users/$user");
    }

    public function createProfile($user)
    {
        $data = request()->validate($this->validationBag());

        $profile = Profile::create([
            'user_id' => $user,
            'description' => $data['description'],
            'url' => $data['url'],
        ]);

        if (isset($data['image'])) {
            $this->updateProfileImage($data['image'], $profile);
        }

        return redirect("/users/$user");
    }

    private function updateProfileImage(UploadedFile $image, Profile $profile) {

        tap($profile->image, function ($previous) use ($image, $profile) {
            $profile->forceFill([
                'image' => $image->storePublicly(
                    'profile-photos', ['disk' => 'public']
                ),
            ])->save();

            if ($previous) {
                Storage::disk('public')->delete($previous);
            }
        });
    }

    private function validationBag()
    {
        return [
            'description' => '',
            'url' => '',
            'image' => '',
        ];
    }

Assumed routes.

Route::put('/users/{user}/profile', [UserController::class, 'updateProfile'])->name('update.user.profile');
Route::post('/users/{user}/profile', [UserController::class, 'createProfile']);
Route::get('/users/{user}', [UserController::class, 'index']);

Edit your <form> action attribute to:

<form action="{{'/users/'. $user->id. '/profile'}}" enctype="multipart/form-data" method="post">

I hope you have this route /users/{$user} defined in your routes/web.php file.

Add a $fillable member variable to AppModelsProfile model.i.e:

protected $fillable = ["user_id", "description", "url", "image"];

Not really related to the question, but you may also want to add a foreign key constraint to the CreateProfilesTable migration. i.e:

$table->foreignId("user_id")->constrained();

Since it’s a one-to-one relationship, it also makes sense to add a unique key constraint on user_id column in ‘profiles table ‘(CreateProfilesTable migration). i.e:

$table->string('user_id')->unique();
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement