如何通过应用程序中的表单为用户创建配置文件?我正在使用 laravel 8

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

大家好,我是 Laravel 的新手。

我的数据库中有一个用户,有姓名、用户名和密码,现在我想通过我的应用程序中的表单为该用户创建一个配置文件,用户可以在其中提交配置文件图像、描述和 url.我还希望用户能够在之后编辑和提交更改。我该怎么做?

我在 web.php 的路线是:

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

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

我在edit.blade.php文件中的表格如下:

<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>

最后,我在 UserController 中的方法是:

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}');
}

为了提供更好的上下文,配置文件描述、url 和图像列不在我的用户模型中,而是在我的配置文件模型中。我已经定义了这些关系,以便更好地理解以下文件的代码:

这是我的 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;

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);
    }
}

这是用户 Table 文件:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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');
    }
}

这是 Profile.php 文件:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    use HasFactory;

    protected $guarded = [];

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

这是配置文件 Table 文件:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

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');
    }
}

您需要验证传入的 $request 对象,请尝试将控制器的更新函数代码更改为:

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

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

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

看看文档,它们很有帮助:https://laravel.com/docs/8.x/validation#quick-writing-the-validation-logic

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 App\Models\Profile;
    use Illuminate\Http\UploadedFile;
    use Illuminate\Support\Facades\Storage;
    use Illuminate\Http\Request;

    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' => '',
        ];
    }

假定路线。

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']);

将您的 <form> action 属性编辑为:

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

希望您在 routes/web.php 文件中定义了这条路线 /users/{$user}

App\Models\Profile增加一个$fillable成员变量model.i.e:

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

与问题没有太大关系,但您可能还想向 CreateProfilesTable 迁移添加外键约束。即:

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

由于是一对一的关系,在'profiles table '(CreateProfilesTable迁移)的user_id列上添加唯一键约束也是有意义的。即:

$table->string('user_id')->unique();
$data = $this->validate($request, [
            'description' => 'nullable',
            'url' => 'required',
            'image' => 'nullable',
        ]);

        $user->description= $data['description'];
        $user->url= $data['url'];
        $user->image= $data['image'];

        $user->save();