Saving Images to a Tailor Record with Validation in yaml file

I have a dilemma. I’m trying to add images to a NEW tailor entry with this code, more or less:

$rental         = EntryRecord::inSection('Rentals\Rentals');
$rental->images = files('images');
// I've also tried $rental->images = (array) files('images');
$rental->save();

Problem is, everytime I try to save the file, the validation triggers saying that the image is required. I did dd($rental->images) (before the save method) and sure enough, it returns empty:

October\Rain\Database\Collection {#1687 // storage/cms/cache/60/e6/create_rental.htm.php:84
  #items: []
  #escapeWhenCastingToString: false
}

Does anyone know why this isn’t working?

For the record, I followed this documentation: File Attachments - October CMS - 3.x

hmm, did u tested it with that (array) retyping?

$rental         = EntryRecord::inSection('Rentals\Rentals');
$rental->images = (array) files('images');
// I've also tried $rental->images = (array) files('images');
$rental->save();

Is mentoined in

Handling Multiple Attachments

For multiple attach relations ($attachMany), you can pass an array of values from the files() function.

$model->photos = (array) files('multi_file');

Yeah I did. What’s weird is without the validation, it saves fine. But with the validation, it does not. I had to create a custom validation in my php code field for it to work, but this may be a bug.

can u share your model class and rules definitions?

I don’t have a model class since I’m using Tailor’s built in functions to create the data. The validation rules are here:

uuid: 10b46eed-a314-11ed-adae-4836e528207e
handle: Rentals\Rentals
type: stream
name: Rentals
drafts: true
multisite: sync

primaryNavigation:
  icon: icon-home
  order: 140

navigation:
  parent: Rentals\Rentals
  icon: icon-home
  order: 140

customMessages:
  buttonCreate: New Rental

fields:
  slug:
    validation: 
     - required
     - unique:xc_10b46eeda31411edadae4836e528207ec,slug
  content:
    type: richeditor
    span: adaptive
    label: Property Description
    tab: Description
    column: false
    validation: required
  location:
    type: entries
    source: Rentals\Locations
    label: General Location
    tab: Location
    commentAbove: Select the general location of your rental property. Used for the rental search and throughout the site
    span: left
    column: false
    validation: required_if:show_map,true
  address:
    label: Address
    type: addressfinder
    commentAbove: Add the address of the Rental Location
    tab: Location
    span: right
    validation: required
    column:
      label: Address
      type: Text
  show_map:
    label: Show Map
    comment: Show the map for this rental
    span: right
    tab: Location
    type: switch
    column: false
    scope: false
  hr_location:
    type: ruler
    tab: Location
    trigger:
      action: show
      field: show_map
      condition: checked
  general_location:
    label: General Location
    type: addressfinder
    commentAbove: Choose a location that is close to the rental. Used for the map fields below. You can also set the Latitude & Longitude Directly
    tab: Location
    span: left
    column: false
    validation: required_if:show_map,true
    trigger:
      action: show
      field: show_map
      condition: checked
    fieldMap:
      latitude: latitude
      longitude: longitude
  map_zoom_level:
    label: Map Zoom Level
    type: number
    min: 0
    max: 18
    default: 12
    tab: Location
    span: right
    column: false
    commentAbove: Choose the zoom level of the map on the page
    trigger:
      action: show
      field: show_map
      condition: checked
  latitude:
    type: text
    label: Latitude
    tab: Location
    span: left
    column: false
    validation: required_if:show_map,true
    trigger:
      action: show
      field: show_map
      condition: checked
  longitude:
    type: text
    label: Longitude
    tab: Location
    span: right
    column: false
    validation: required_if:show_map,true
    trigger:
      action: show
      field: show_map
      condition: checked
  location_info:
    type: richeditor
    label: Location Information
    commentAbove: Add information about this location. Appears right after the map.
    tab: Location
    column: false
  ical_url:
    label: iCal URL
    commentAbove: The link to the bookings related to this rental. Used to block off other calendars (Airbnb, VRBO, etc)
    type: partial
    path: "#/adeluxurystay/partials/fields/_ical_url"
    tab: Rental Info
  max_guests:
    type: number
    label: Max No. of Guests
    commentAbove: The Max number of guests for this rental
    tab: Rental Info
    span: left
    column: false
    validation:
      - "required"
  minimum_stay:
    type: number
    label: Minimum Stay
    span: right
    tab: Rental Info
    commentAbove: The minimum number of days the person can book.
    column: false
    validation:
      - required
  booking_buffer:
    type: number
    label: Booking Buffer
    span: left
    tab: Rental Info
    commentAbove: The minimum number of days the person can book from today. Set to 0 for same day booking.
    column: false
    validation:
      - required
  featured:
    type: switch
    label: Featured
    comment: Feature this rental property throughout the site.
    tab: Rental Info
    span: right
    cssClass: mt-0 mt-lg-5
  hr_1:
    type: ruler
    tab: Rental Info
  images:
    type: fileupload
    validation: required
    mode: image
    label: Images
    commentAbove: Images for this rental property. The first image is used as the main image
    tab: Rental Info
    scope: false
    column:
      label: Images
      type: image
      sortable: true
  hr_2:
    type: ruler
    tab: Rental Info
  beds:
    type: number
    label: No. of Beds
    commentAbove: The number of beds in this rental
    tab: Rental Info
    span: row
    spanClass: col-12 col-lg-4
    validation: required
  bedrooms:
    type: number
    label: No. of Bedrooms
    commentAbove: The number of bedrooms in this rental
    tab: Rental Info
    span: row
    spanClass: col-12 col-lg-4
    validation: required
  bath:
    type: number
    label: No. of Bath
    commentAbove: The number of baths in this rental
    tab: Rental Info
    span: row
    spanClass: col-12 col-lg-4
    validation: required
  hr_3:
    type: ruler
    tab: Rental Info
  amenities:
    type: entries
    label: Amenities
    source: Rentals\Amenities
    commentAbove: Available amenities for this rental property.
    tab: Rental Info
    span: left
    quickselect: true
    column: false
    scope: false
  house_rules:
    type: entries
    label: House Rules
    source: Rentals\HouseRules
    commentAbove: Add House Rules to this rental property.
    tab: Rental Info
    span: right
    quickselect: true
    column: false
    scope: false
  check_in_instructions:
    type: richeditor
    label: Check In Instructions
    commentAbove: Add specific check in instructions for this rental
    tab: Rental Info
    column: false
    validation: required
  email_header:
    type: richeditor
    label: Email Header
    commentAbove: Add content to the beginning of the email when the guests' booking is complete.
    tab: Rental Info
    span: left
    column: false
    validation: required
  email_footer:
    type: richeditor
    label: Email Footer
    commentAbove: Add content to the end of the email when the guests' booking is complete.
    tab: Rental Info
    span: right
    column: false
    validation: required
  price:
    type: number
    label: Price
    commentAbove: The default price per night for this rental unit
    span: left
    tab: Pricing & Options
    validation: required
    column:
      label: Price
      type: number
      format: "$%.2f"
  price_per_extra_guest:
    type: number
    label: Price per Extra Guest
    commentAbove: Add a fee on top of the default price per extra guests.
    span: right
    tab: Pricing & Options
    column:
      label: Price per Extra Guest
      type: number
      format: "$%.2f"
  deposit:
    label: Deposit (Incidental Fee)
    type: number
    tab: Pricing & Options
    commentAbove: Hold an amount on the the customer's card. Amount refunded is determined by the booking settings. Used when the user books. If not set, Defaults to the Global Deposit.
    span: left
    valdiation: required
  services:
    type: entries
    label: Services
    commentAbove: Associated services for this rental that the customer can add.
    displayMode: taglist
    inverse: rentals
    source: Rentals\Services
    tab: Pricing & Options
    hidden: false
    span: right
  hr_4:
    tab: Pricing & Options
    type: ruler
  fees:
    type: repeater
    label: Fees
    commentAbove: The fees associated with this rental
    tab: Pricing & Options
    span: left
    column: false
    form:
      fields:
        name:
          type: text
          label: Fee Name
          span: row
          spanClass: col-12 col-lg-4
        price:
          type: number
          label: Price
          span: row
          spanClass: col-12 col-lg-4
        pricing_style:
          type: dropdown
          label: Pricing Style
          span: row
          spanClass: col-12 col-lg-4
          options:
            fixed: Fixed
            percent: Percent
  availability:
    type: repeater
    label: Availability
    tab: Pricing & Options
    commentAbove: Add specific Pricing per day.
    prompt: Add Availability
    span: right
    readOnly: true
    form:
      fields:
        check_in:
          type: datepicker
          mode: date
          format: M d, Y
          useTimezone: true
          label: Check In
          span: left
          default: today
          readOnly: true
        check_out:
          type: datepicker
          mode: date
          format: M d, Y
          useTimezone: true
          label: Check Out
          span: right
          default: today
          readOnly: true
        available:
          type: switch
          label: Available?
          span: left
          comment: Whether or not this rental is available at the above time
          cssClass: mt-0 mt-lg-4
          default: 1
          readOnly: true
        price:
          type: number
          label: Price
          span: right
          readOnly: true
          trigger:
            action: show
            condition: checked
            field: available
  cancellation:
    type: switch
    label: Allow Cancellation?
    comment: Allow cancellation for this rental
    tab: Cancellation & Updates
    span: left
    default: true
    column: false
    scope:
      label: Cancellable
  free_24_hour_cancellation:
    type: switch
    label: Free 24 Hour Cancellation
    comment: Disable fees if the customer cancels the booking within 24 Hours.
    tab: Cancellation & Updates
    span: right
    default: true
    column: false
    trigger:
      action: show
      field: cancellation
      condition: checked
    scope:
      label: 24hr Hour Cancellation
  cancellation_fees:
    type: repeater
    tab: Cancellation & Updates
    label: Cancellation fees
    commentAbove: Apply fees based on when the booking is cancelled.
    column: false
    form:
      fields:
        days:
          type: number
          label: Days
          comment: Number of days before the check in date where the cancellation incurs a fee.
          span: row
          spanClass: col-12 col-lg-4
        price:
          type: number
          label: Price
          span: row
          spanClass: col-12 col-lg-4
        pricing_style:
          type: dropdown
          label: Pricing Style
          span: row
          spanClass: col-12 col-lg-4
          options:
            fixed: Fixed
            percent: Percent
    trigger:
      action: show
      condition: checked
      field: cancellation
  stay_reduction_fees:
    type: repeater
    tab: Cancellation & Updates
    label: Stay Reduction Fees
    commentAbove: Apply fees based on when the booking stay length is reduced.
    column: false
    form:
      fields:
        days:
          type: number
          label: Days
          comment: Number days before their check in date where their stay reduction incurs a fee.
          span: row
          spanClass: col-12 col-lg-4
        price:
          type: number
          label: Price
          span: row
          spanClass: col-12 col-lg-4
        pricing_style:
          type: dropdown
          label: Pricing Style
          span: row
          spanClass: col-12 col-lg-4
          options:
            fixed: Fixed
            percent: Percent
  cancellation_form:
    label: Cancellation Form
    commentAbove: This form will be used if the user wishes to cancel. Leave empty to use the global cancellation form
    type: entries
    source: Content\Forms
    tab: Cancellation & Updates
    maxItems: 1
    displayMode: recordfinder
    span: left
    title: Find Form
    column: false
    scope: false
  check_in_date_buffer:
    label: Check-in Date Buffer
    commentAbove: Choose the amount of days before the check-in date that they can change the check-in date.
    type: number
    column: false
    scope: false
    tab: Cancellation & Updates
    span: right
    default: 1
  ical_urls:
    type: repeater
    commentAbove: Add iCal URLS to your rental to block off previously booked time from other platforms
    label: iCal URLs
    tab: Calendar Sync
    column: false
    form:
      fields:
        name:
          label: Name
          type: text
          tab: Calendar Sync
          span: left
          commentAbove: The name of the iCal URL. Used for the Backend Dashboard.
        ical_url:
          type: text
          label: iCal URL
          tab: Calendar Sync
          commentAbove: Enter your iCal URL to Check availability against other platforms and apply them here.
          span: right
          column: false
        color: 
          type: colorpicker
          label: Color
          commentAbove: Choose a color for this event. Used for the Backend Dashboard.
  bookings:
    type: entries
    label: Bookings
    commentAbove: Associated bookings for this rental
    inverse: rental
    source: Bookings\Bookings
    tab: Bookings
    hidden: true
    column: false
    scope: false
  owner_type:
    type: dropdown
    label: Owner Type
    commentAbove: Specify the owner type of the rental. Used for rental administrative tasks.
    tab: Manage
    span: left
    options:
      user: User (From User Plugin)
      admin: Admin (From Backend)
  user_owner:
    label: Owner
    type: recordfinder
    list: ~/plugins/rainlab/user/models/user/columns.yaml
    recordsPerPage: 10
    title: Find User
    tab: Manage
    descriptionFrom: email
    useRelation: false
    modelClass: RainLab\User\Models\User
    disabled: false
    span: right
    commentAbove: The owner of this rental.
    conditions: rental_owner = 1
    validation: required_if:owner_type,user
    column:
      label: User
      type: partial
      path: "#/adeluxurystay/partials/columns/_user"
    trigger:
      action: show
      condition: value[user]
      field: owner_type
  admin_owner:
    label: Owner
    type: recordfinder
    list: ~/modules/backend/models/user/columns.yaml
    recordsPerPage: 10
    title: Find User
    tab: Manage
    nameFrom: first_name
    descriptionFrom: email
    useRelation: false
    modelClass: Backend\Models\User
    disabled: false
    span: right
    commentAbove: The owner of this rental.
    conditions: is_activated = true
    validation: required_if:owner_type,admin
    column:
      label: User
      type: partial
      path: "#/adeluxurystay/partials/columns/_user"
    trigger:
      action: show
      condition: value[admin]
      field: owner_type
  booking_email_recipient:
    label: Booking Email Recipient
    commentAbove: Specify an email to send the admin emails. Leave empty to use the rental owner's email.
    type: text
    tab: Admin Emails
    span: row
    spanClass: col-12 col-lg-6
    validation:
      - email
  booking_email_recipient_name:
    label: Booking Email Recipient's Name
    commentAbove: Specify an name to send the admin emails. Leave empty to use the rental owner's email.
    type: text
    tab: Admin Emails
    span: row
    spanClass: col-12 col-lg-6
    validation: required_with:booking_email_recipient
  booking_email_recipient_phone:
    label: Booking Email Recipient's Phone
    commentAbove: Specify a phone number for rental emails. Used for support. Leave empty to use the rental owner's email.
    type: text
    tab: Admin Emails
    span: row
    spanClass: col-12 col-lg-6
    validation: required_with:booking_email_recipient
  seo_fields:
    tab: SEO
    type: mixin
    name: SEO Fields
    source: Content\SEOFields
  code_fields:
    tab: SEO
    type: mixin
    name: SEO Fields
    source: Content\CodeFields

Ok, i seee…
you have 2 things here.

1st:
probably, when you tried creating this “booking” from backend, everything works fine, right?

2nd:
you tried to create programatically new item, then, before you call $newModel->save() you need to attach that image, not after that. then only one change here is needed.

// creating a file reference
   $file = new System\Models\File;
   $file = $file->fromPost(Input::file('files'));  // insert your input name for that field
   $file->save();

   // Attach the uploaded file to your model
   $model = new EntryRecord::inSection('Rentals\Rentals');
   $model->file()->add($file); 
   $model->save();

Yeah it works fine from the backend

So I did try that but I had to alter it a bit. I’m using Multuple images so I came up with this:

    if (!empty(files('images'))) {
        foreach (files('images') as $image) {    
            $file = new System\Models\File;
            $file = $file->fromPost($image); 
            $file->save();
            $rental->images->add($image);
        }
    }

Now this gets past the validation, but does NOT save the images to the newly created record

check this … just wrong variable…

    if (!empty(files('images'))) {
        foreach (files('images') as $image) {    
            $file = new System\Models\File;
            $file = $file->fromPost($image); 
            $file->save(); /// <<<  here you create a $file
            $rental->images->add($image);  //// <<<< here you try to attach $image!
        }
    }

just rewrite it to

$rental->images->add($file);

I did miss the variable :man_facepalming: , thank you.

But for some reason the images are still not saving. What I have now is:

if (!empty(files('images'))) {
    foreach (files('images') as $image) {    
        $file = new System\Models\File;
        $file = $file->fromPost($image); 
        $file->save();
        $rental->images->add($file);
    }
}

$rental->save();

hmm… tried to simplify your blueprint… to get only images and title to be present…

$rental = \Tailor\Models\EntryRecord::inSection('Rentals\Rentals');
$rental->title = "testing " . time();    
$rental->images = files('images'); 
$rental->save();

only one thing what i removed was that validation: required …
probably, there are issue with checking, because it is not “real field” but attachment, then validation just must be replaced with requried option.

  images:
    type: fileupload
    required: true // <<< replaced validation: required 
    mode: image

this will :

  • allow user in backend to have that red dot as “required” (without validation)
  • allow users to upload and process images without that validation.

currently i think, this may be a good thing to raise an issue in tailor, when is created attach relations, to rewamp validation on attachment, instead of input field. @daft

1 Like