简体   繁体   中英

Laravel select models based on relationship

I am working on a library application and I want to create a function where the user can rent out a book to a customer. However, I want the books that are rented out right now to not show up in the select box when renting out another book. I have looked up several articles about this, but couldn't really make up a solution so I would be happy about any help. The idea is, that when a book has the attribute "maxreturndate" set it won't show up.

CheckedOutController:

    <?php

    namespace App\Http\Controllers;

    use Illuminate\Http\Request;
    use App\CheckedOut;
    use App\Book;
    use App\Reader;



    class CheckedOutController extends Controller
    {
        /**
         * Display a listing of the resource.
         *
         * @return \Illuminate\Http\Response
         */
        public function index()
        {
            $checkedOuts = CheckedOut::with(['book', 'reader'])->get();

            return view('checkedouts/index', compact('checkedOuts'));
        }

        /**
         * Show the form for creating a new resource.
         *
         * @return \Illuminate\Http\Response
         */
        public function create()
        {
            $books = Book::all();
            $readers = Reader::all();

            return view('checkedouts/create', compact('books','readers'));
        }

        /**
         * Store a newly created resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @return \Illuminate\Http\Response
         */
        public function store(Request $request)
        {
            $validatedData = $request->validate([
                'book_id' => 'required',
                'reader_id' => 'required',
                'maxreturndate' => 'required|date',
                'returndate' => 'nullable',
                ]);

                $checkedOut = CheckedOut::create($validatedData);

                return redirect('checkedouts')->with('success', 'Buch wurde erfolgreich verliehen!');
        }

        /**
         * Display the specified resource.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function show($id)
        {
            //
        }

        /**
         * Show the form for editing the specified resource.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function edit($id)
        {
            //
        }

        /**
         * Update the specified resource in storage.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function update(Request $request, $id)
        {
            //
        }

        /**
         * Remove the specified resource from storage.
         *
         * @param  int  $id
         * @return \Illuminate\Http\Response
         */
        public function destroy($id)
        {
            //
        }
    }

index.blade.php

@extends('layout')


@section('title')
<title>Alle ausgeliehen Bücher</title>
@section('content')
<style>
  .uper {
    margin-top: 40px;
  }
</style>
<div class="uper">
  @if(session()->get('success'))
  <div class="alert alert-success">
    {{ session()->get('success') }}
  </div><br />
  @endif

  <table class="table table-hover">
    <thead>
      <tr>
        <td>ID</td>
        <td>Titel</td>
        <td>Verliehen an</td>
        <td>Verleihdatum</td>
        <td>Fällig am</td>
        <td>Zurückgebracht am</td>
        <td colspan="2">Funktionen</td>
      </tr>
    </thead>
    <tbody>
      @foreach($checkedOuts as $checkedOut)
      <tr>
        <td>{{$checkedOut->id}}</td>
        <td>{{$checkedOut->book->title}}</td>
        <td>{{$checkedOut->reader->name}}</td>
        <td>{{$checkedOut->created_at}}</td>
        <td >{{$checkedOut->maxreturndate}}</td>
        <td>{{$checkedOut->returndate}}</td>
        <td></td>

        <td><a href="{{ route('checkedouts.edit', $checkedOut->id)}}" class="btn btn-primary">Bearbeiten</a></td>
        <td><a href="{{ route('checkedouts.show', $checkedOut->id)}}" class="btn btn-primary">Anzeigen</a></td>
        <td>
          <form action="{{ route('checkedouts.destroy', $checkedOut->id)}}" method="post">
            @csrf
            @method('DELETE')
            <button class="btn btn-danger" type="submit">Löschen</button>
          </form>
        </td>
      </tr>
      @endforeach
    </tbody>
  </table>
  <div>
    @endsection

Migrations:

<?php

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

class CreateCheckedOutsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('checked_outs', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->bigInteger('book_id')->unsigned();
            $table->foreign('book_id')->references('id')->on('books')->onDelete('cascade');
            $table->bigInteger('reader_id')->unsigned();
            $table->foreign('reader_id')->references('id')->on('readers')->onDelete('cascade');
            $table->date('maxreturndate');
            $table->date('returndate')->nullable();
            $table->timestamps();
        });
    }

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

create.blade.php

@extends('layout')


@section('title')
<title>Buch verleihen</title>
@section('stylesheets')
<script src="http://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css" rel="stylesheet" />


@endsection
@section('content')
<style>
    .uper {
        margin-top: 40px;
    }
</style>
<div class="card uper">
    <div class="card-header">
        Buch verleihen
    </div>
    <div class="card-body">
        <form method="post" action="{{ route('checkedouts.store') }}">
            <div class="form-group">
                @csrf
                <label for="book_id">Buch:</label>
                <select name="book_id" class="form-control select2-single <!-- @error('book_id') is-invalid @enderror -->">
                    @foreach ($books as $book)
                    <option value="{{ $book->id }}">{{ $book->title }}</option>
                    @endforeach
                </select>
                @error('book_id')
                <div class="alert alert-danger">{{ $message }}</div>
                @enderror
            </div>

            <div class="form-group">
                <label for="reader_id">Verleihen an:</label>
                <select name="reader_id" class="form-control select2-single <!-- @error('reader_id') is-invalid @enderror -->">
                    @foreach ($readers as $reader)
                    <option value="{{ $reader->id }}">{{ $reader->name }}</option>
                    @endforeach
                </select>
                @error('reader_id')
                <div class="alert alert-danger">{{ $message }}</div>
                @enderror
            </div>

            <div class="form-group">
                <label for="maxreturndate">Zurückbringen bis:</label>
                <input type="date" class="form-control" name="maxreturndate" />
                @error('name')
                <div class="alert alert-danger">{{ $message }}</div>
                @enderror
            </div>

            <button type="submit" class="btn btn-primary">Verleihen</button>
        </form>
    </div>
</div>
<script type="text/javascript">
    $(".select2-single").select2();
</script>

@endsection

The relationship between the 3 Models:

Book:

public function checkedOut(){
    return $this->hasOne(CheckedOut::class);
}

Reader:

public function checkedOut()
    {
        return $this->belongsTo(CheckedOut::class);
    }

CheckedOut:

public function book(){
    return $this->belongsTo(Book::class);

}

public function reader(){
    return $this->belongsTo(Reader::class);

}

My suggestion is to set up Books and Readers with a many-to-many relationship . Now, your models could look like this:

<?php
namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;

class Book extends Model
{
    public function readers()
    {
        return $this
            ->belongsToMany(\App\Reader::class, 'checked_outs')
            ->using(\App\Checkout::class)
            ->withPivot(['returndate', 'maxreturndate']);
    }
}

class Reader extends Model
{
    public function books()
    {
        return $this
            ->belongsToMany(\App\Book::class, 'checked_outs')
            ->using(\App\Checkout::class)
            ->withPivot(['returndate', 'maxreturndate']);
    }
}

class Checkout extends Pivot
{
    // this should be named `book_reader` but we override it here
    $table = "checked_outs";

    $dates = [
        "maxreturndate",
        "returndate",
    ];
}

I have created a pivot model which isn't necessary, but allows you to operate directly on the pivot table and cast the extra attributes to dates automatically. Whether this is useful is a matter of opinion. You should rename the checked_outs table to book_reader which is the naming convention that Laravel expects for a pivot table.

Getting the books that aren't checked out is simple enough to do as follows, using the doesntHave method to check for absence of a relationship.

public function create()
{
    $books = Book::doesntHave("readers")->get();
    $readers = Reader::all();

    return view('checkedouts/create', compact('books','readers'));
}

And "checking out" a book could look like this:

public function store(Request $request)
{
    $reader = Reader::find($request->reader_id);
    $reader
        ->books()
        ->attach(
            $request->book_id,
            ["returndate" => Carbon\Carbon::now()->addDays(7)]
        );
}

when a book has the attribute "maxreturndate" set it won't show up

Since you did not specify in your migration, I am going to assume here that you have a maxreturndate nullable field in your books table, then you should be able to simply create a local scope when you want your list of "not rented" books.

Here's an example on creating a notRented scope:

// in your Book model define the local scope
public function scopeNotRented($query){
    return $query->whereNotNull('maxreturndate');
}

// in the create method of your controller
public function create()
{
    $books = Book::notRented()->get();
    $readers = Reader::all();

    return view('checkedouts/create', compact('books','readers'));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM