# Admin: records

  • We already created a admin/records page in the Eloquent models section
  • This page was not meant to be a real admin page, but was only to show how to use Eloquent models and was build with a regular Laravel view
  • In this chapter we will create a new records page with CRUD operations and this time we build it with Livewire

REMARK

Remember that you can always reset the database to its initial state by running the command:

php artisan migrate:fresh
1

# Preparation

# Create a Records component

  • Create a new Livewire component with the terminal command php artisan make:livewire Admin/Records
    • app/Http/Livewire/Admin/Records.php (the component class)
    • resources/views/livewire/admin/records.blade.php (the component view)
  • Open the component class and add the vinylshop layout





 
 
 
 



class Records extends Component
{
    public function render()
    {
        return view('livewire.admin.records')
            ->layout('layouts.vinylshop', [
                'description' => 'Manage the records of your vinyl shop',
                'title' => 'Records',
            ]);
    }
}
1
2
3
4
5
6
7
8
9
10
11

# Add a new route

  • Add a new get-route for the admin/records to the routes/web.php file and change the route to the old get-route torecords_old and it's name to records.old
  • Update the navigation menu in resources/views/livewire/layout/nav-bar.blade.php

# Basic scaffolding

  • Let's start with a basic scaffolding for the view and the component
  • After the previous chapter, we already have a good idea of which properties and methods we'll need in the component to create a CRUD page
  • Additional properties and methods can be added later

# Jetstream dialog modal

REMARKS

  • The Jetstream dialog modal (opens new window) is a component that is used to show/hide a modal with a title, content and footer
  • More specifically, it's a component that's build on top of a second component
    • Ctrl + click on the x-jet-dialog-modal tag to see the code of the first component
      • This component contains tree slots ($title, $content and $footer) that are wrapped in inside a x-jet-modal component
    • Ctrl + click on the x-jet-modal tag to see the code of the second component
      • The most important part of this component is show: @entangle($attributes->wire('model')).defer and Alpines x-show directive
      • This is where the connection between the modal and our $showModal property is made
  • Now we have two ways to hide the modal with the 'CANCEL' button in the footer of our modal
    1. Using Livewire: <x-jet-secondary-button wire:click="$set('showModal', false)">Cancel</x-jet-secondary-button>
      This is the slower way because it will first update the $showModal property on the backend and then the modal will be hidden on the frontend
    2. Using Alpine: <x-jet-secondary-button @click="show = false">Cancel</x-jet-secondary-button>
      This is the faster way because it will first hide the modal in the frontend, and then it will update the $showModal property on the backend
  • Add the @click="show = false" to the 'CANCEL' button in the footer of the modal and check the result









 



<x-jet-dialog-modal id="recordModal"
    wire:model="showModal">
    <x-slot name="title">
        <h2>title</h2>
    </x-slot>
    <x-slot name="content">
        content
    </x-slot>
    <x-slot name="footer">
        <x-jet-secondary-button @click="show = false">Cancel</x-jet-secondary-button>
    </x-slot>
</x-jet-dialog-modal>
1
2
3
4
5
6
7
8
9
10
11
12

# Read all records

# Show all the records in the table

# Add pagination to the table

# Filter by artist or title

# Filter by records in stock

  • The <x-tmk.form.switch id="noStock" ... /> is just a wrapper around a checkbox
  • The state of this checkbox determines whether we should filter or not
    • If checked, filter the query further by ->where('stock', '=', 0)
    • If unchecked, skip this filter

# Filter by records without cover image

  • The <x-tmk.form.switch id="noCover" ... /> acts just the same as the switch for $noStock
    (if true: filter, if false: skip filter)
  • The problem is that we don't have a column for the cover in the database!
    • Yes, we have a "generated" columncover, but this is only available AFTER the ->get() (or ->paginate()) method is called
    • So, we can't use this in our filter 😔
  • Wath we can do, is make a scope for this in the Record model and use it in our query

# Create a new record

# Find the MusicBrainz ID (mb_id) of the record

  • Go to https://musicbrainz.org/ (opens new window)
  • Search, in the right top corner, an artist (e.g. The Doors)
  • Click on the artist name
    Search artist
  • Now click on one of the albums (e.g. L.A. Woman)
  • You get a list of all the releases of this album. Click on one of them (be sure to choose for a release on vinyl!).
  • The property mb_id in our records table is the code at the end of the URL (e.g. e68f23df-61e3-4264-bfc3-17ac3a6f856b)
    Search record

# Get the data from MusicBrainz API

  • Now that we have the mb_id of the record, we can use the MusicBrainz API (opens new window) to extract the data from the record
  • We need: the title of the record, the artist and the cover (if there is one)
    These fields are not editable, except for the cover later in this course
  • We also need the price of the record, how many items are in stock and the genre this record belongs to
    These fields are editable and don't have anything to do with the MusicBrainz API
  • Some examples on how the extract the data that we need from the JSON response:

# Add form fields to the modal

  • Our modal needs to have the following fields:
    • mb_id (text input)
    • title and artist (hidden inputs because they are not editable)
    • genre (select input with all the genres)
    • price and stock (number inputs)
  • Get all the genres
    • We need to get all the genres from the database and put them in a select input
    • Because this list don't change while we are on this page, we can get it once (use the mount() methode for this, not the render() methode) and store it in a new $genres property


 



 
 
 
 
 




class Records extends Component
{
      public $genres;
      
      ...
      
      // get all the genres from the database (runs only once)
      public function mount()
      {
        $this->genres = Genre::orderBy('name')->get();
      }
      
      public function render() { ... }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • Now we can populate the modal

# Add the validation rules

# Update the validation messages

# Get the necessary data from the MusicBrainz API

# Save the cover image to the server

  • With only two extra lines of code, we can save the cover image to the server

# Save the record to the database

  • Now that we have all the data we need, we can save the record to the database

# Update a record

  • All we have to do to edit a record is to set the values of the $newRecord array to the values of the record we want to edit
  • This can be done by passing the record id to the setNewRecord() method, and then we can also re-use the modal to update the record
  • We're only allowed to update the genre_id, the stock and the price fields
  • The mb_id, the title and the artist fields are read-only (updating the mb_id would mean that we create a new record)

# Enter edit mode

# Refactor the modal

  • We can use e.g. the value of the newRecord.id property to determine if we're in edit mode or not
    • newRecord.id is empty: we're in create mode
    • newRecord.id is not empty: we're in edit mode

# Update the record

# Delete a record

# EXERCISES:

# 1: Background color

  • Give all the records that are out of stock a red background color out of stock

# 2: Delete the cover image from the server

  • Update the deleteRecord() method so that the cover image is also deleted from the server

# 3: Jetstream confirmation modal

  • Jetstream has actually two modal components: x-confirmation-modal and x-dialog-modal
    (see resources/views/vendor/jetstream/components/)
  • Examine the code for the confirmation modal and try to use them to confirm the deletion of a record
  • TIPS:
    • add a new property to toggle the modal
    • add a new method to update some values in the $newRecord property, so they can be used in the modal
      Delete confirmation modal

# 3: Clear the search field

  • Placed a little X over the input field to clear the search field
  • The X is only visible when search field in not empty
  • Tip: see Shop component for an example Clear search field
Last Updated: 12/14/2022, 6:12:16 AM