使用Ajax输入在Laravel中创建透视表值

I'm learning Laravel by creating a recipe website.

The idea is a user creates a recipe which includes a title, description and number of portions (and tags), and then is directed to a new view in which they add the ingredients.

I've got this working, and the user can successfully create the recipe and the ingredients, which are being written to their respective tables, but I'm unable to attach/sync them.

Relevant parts of the models:

Recipe Model:

class Recipe extends Model
{
  public function ingredients(){
    return $this->hasMany('App\Ingredient', 'recipe_ingredients');
  }
}

Ingredient Model:

class Ingredient extends Model
{

  public function recipe(){
    return $this->belongsTo('App\Recipe', 'recipe_ingredients');
  }
}

Ingredients Controller

public function store(Request $request)
{

  $this->validate($request, [
    'name' => 'required|max:255'
  ]);

  $ingredient = new Ingredient;
  $ingredient->name = $request->name;
  $ingredient->save();

  $recipe = Recipe::find($request->id);

  $recipe->ingredients()->attach($recipe_id);

  $data = [
    'success' => true,
    'message'=> 'Your AJAX processed correctly',
    'name' => $ingredient->name,
    'recipe' => $recipe
  ] ;

    return response()->json($data);
}

If I remove the $recipe->ingredients()->attach($recipe_id); the ingredients save to the ingredients table, but I can't get the recipe_id and ingredient_id to save in the recipe_ingredients table`.

I think I'm using the attach wrong, but I could be wrong.

Note: Not that I think it makes any difference, but I'm submitting the data via Ajax.

Script:

$(document).ready(function(){
  $("#submit").click(function() {
    var name = $("#ingredientName").val();
    var token = $("#token").val();

    $.ajax({
      headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        },
      type: "post",
      data: "name="+name,
      dataType:'json',
      url: "{{ route('ingredients.store', ['id' => $recipe->id]) }}",
      success:function(data){
        console.log(data);
        $("#msg").html('<div class="alert alert-success my-0">'+data.name+' added</div>');
        $("#msg").toggleClass("invisible")
        $("#msg").fadeOut(2000);
        $("#ingredientsTable").append('<tr><td scope="col" class="align-middle">'+data.name+'</td></tr>');
      }
    });
  })
})

Revised Controller

public function store(Request $request)
{

  $this->validate($request, [
    'name' => 'required|max:255'
  ]);

  $ingredient = new Ingredient;
  $ingredient->name = $request->name;
  $ingredient->save();

  $recipe = Recipe::find($request->id);
  $recipe->ingredients()->attach($ingredient->id);

  $data = [
    'success' => true,
    'message'=> 'Your AJAX processed correctly',
    'name' => $ingredient->name,
    'recipe' => $recipe
  ] ;

    return response()->json($data);
}

Table migration

public function up()
{
    Schema::create('recipe_ingredients', function (Blueprint $table) {
        $table->integer('recipe_id')->unsigned();
        $table->foreign('recipe_id')->references('id')->on('recipes');

        $table->integer('ingredient_id')->unsigned();
        $table->foreign('ingredient_id')->references('id')->on('ingredients');
    });
}

As shown by the example here: https://laravel.com/docs/5.6/eloquent-relationships#updating-many-to-many-relationships

You should be attaching the ingredients instead of the recipe:

$recipe->ingredients()->attach($ingredient_id);

-- edit --

You also have your Ingredient model as:

class Ingredient extends Model
{
  public function ingredient(){
      return $this->belongsToMany('App\Recipe', 'recipe_ingredients');
  }
}

However, you should have this instead:

public function recipes(){
    return $this->belongsToMany('App\Recipe', 'recipe_ingredients');
}

You're using the wrong ID when trying to attach the Ingredient to the Recipe:

$ingredient = new Ingredient;
$ingredient->name = $request->name;
$ingredient->save();

$recipe = Recipe::find($request->id);
$recipe->ingredients()->attach($recipe_id);

In the last line, you already have the Recipe, so passing $recipe_id (which I actually don't see defined anywhere) is not the correct logic.

What you need to do is pass the Ingredient you want to attach:

$recipe->ingredients()->attach($ingredient->id);

That should correctly set the relationship.

After making lots of little tweaks and changes, the following finally worked for me:

Recipe /model:

class Recipe extends Model
{
  public function ingredients(){
    return $this->hasMany('App\Ingredient', 'recipe_ingredients', 'recipe_id', 'ingredient_id');
  }

Ingredient Model:

class Ingredient extends Model
{
    public function recipes(){
    return $this->belongsToMany('App\Recipe', 'recipe_ingredients', 'ingredient_id', 'recipe_id');
}

**IngredientsController@Store: **

public function store(Request $request)
{

  $this->validate($request, [
    'name' => 'required|max:255'
  ]);

  $ingredient = new Ingredient;
  $ingredient->name = $request->name;
  $ingredient->save();

  $recipe = Recipe::find($request->id);
  $ingredient->recipes()->attach($recipe->id);

  $data = [
    'success' => true,
    'message'=> 'Your AJAX processed correctly',
    'name' => $ingredient->name,
    'recipe' => $recipe
  ] ;

    return response()->json($data);
}