Repository: livewire/livewire (php)

Issue:
Camelcase properties don't get shortened in validation messages properly
### Description
The attribute name is not shortened when using model property binding if the property name is camelcase.
In the example below naming the property `$companyProfile` results in this bug. Snake case and all lower case work as expected:
```
$company_profile
$companyprofile
```

### Exact steps to reproduce
1. Create the `CompanyProfile` model with a string name column.
2. Create the livewire component and blade template.
3. Without filling the form out, submit and you'll see an error message that reads:
> The company profile.name field is required.


It should read:
> The company name field is required.

### Stripped-down, copy-pastable code snippets
**app/Http/Livewire/UpdateCompanyProfileForm.php**
```php
<?php

namespace App\Http\Livewire;

use App\Models\CompanyProfile;
use Livewire\Component;

class UpdateCompanyProfileForm extends Component
{
    public CompanyProfile $companyProfile;

    protected $rules = [
        'companyProfile.name' => 'required|string',
    ];

    public function mount()
    {
        $this->companyProfile = new CompanyProfile;
    }

    /**
     * Get the view / contents that represent the component.
     *
     * @return \Illuminate\View\View|string
     */
    public function render()
    {
        return view('livewire.update-company-profile-form');
    }

    public function save()
    {
        $this->validate();
    }
}
```

**resources/views/livewire/update-company-profile-form.blade.php**
```blade
<form wire:submit.prevent="save">
    <input
        wire:model.defer="companyProfile.name"        
    />
    @error('companyProfile.name')
        <p>{{ $message }}</p>
    @enderror

</form>
```

### Fix
https://github.com/livewire/livewire/blob/master/src/ComponentConcerns/ValidatesInput.php#L212
```diff
protected function shortenModelAttributes($data, $rules, $validator)
    {
        // If a model ($foo) is a property, and the validation rule is
        // "foo.bar", then set the attribute to just "bar", so that
        // the validation message is shortened and more readable.
        foreach ($rules as $key => $value) {
            $propertyName = $this->beforeFirstDot($key);

            if ($data[$propertyName] instanceof Model) {
-                if (str($key)->replace('_', ' ')->is($validator->getDisplayableAttribute($key))) {
+                if (str($key)->snake()->replace('_', ' ')->is($validator->getDisplayableAttribute($key))) {
                    $validator->addCustomAttributes([$key => $validator->getDisplayableAttribute($this->afterFirstDot($key))]);
                }
            }
        }
    }
```


**Context**
- Livewire version: 2.3.2
- Laravel version: 8.15.0
- Alpine version: 2.7.3
- Browser: Chrome 87.0.4280.67


Pull request description:
fixed property name shortening when camelcased - closes #2079
1️⃣ Is this something that is wanted/needed? Did you create an issue / discussion about it first?
The issue (and fix!) was described in #2079 

2️⃣ Does it contain multiple, unrelated changes? Please separate the PRs out.
No, sir!

3️⃣ Does it include tests, if possible? (Not a deal-breaker, just a nice-to-have)
Yes, I added a test for it. 

4️⃣ Please include a thorough description of the improvement and reasons why it's useful.
Seemed like a valid bug report.

5️⃣ Thanks for contributing! 🙌
🍻 

Patch:
diff --git a/src/ComponentConcerns/ValidatesInput.php b/src/ComponentConcerns/ValidatesInput.php
index 3476f41e..fdd2b7c9 100644
--- a/src/ComponentConcerns/ValidatesInput.php
+++ b/src/ComponentConcerns/ValidatesInput.php
@@ -209,7 +209,7 @@ trait ValidatesInput
             $propertyName = $this->beforeFirstDot($key);
 
             if ($data[$propertyName] instanceof Model) {
-                if (str($key)->replace('_', ' ')->is($validator->getDisplayableAttribute($key))) {
+                if (str($key)->snake()->replace('_', ' ')->is($validator->getDisplayableAttribute($key))) {
                     $validator->addCustomAttributes([$key => $validator->getDisplayableAttribute($this->afterFirstDot($key))]);
                 }
             }


Test patch:
diff --git a/tests/Unit/EloquentModelValidationTest.php b/tests/Unit/EloquentModelValidationTest.php
index 88cdd238..7f5bd62f 100644
--- a/tests/Unit/EloquentModelValidationTest.php
+++ b/tests/Unit/EloquentModelValidationTest.php
@@ -46,6 +46,17 @@ class EloquentModelValidationTest extends TestCase
             ->assertSee('The bar baz field is required.');
     }
 
+    /** @test */
+    public function validate_message_doesnt_contain_dot_notation_if_property_is_camelcased_model()
+    {
+        Livewire::test(ComponentWithCamelCasedModelProperty::class, [
+            'camelFoo' => $foo = CamelFoo::first(),
+        ])  ->set('camelFoo.bar', '')
+            ->call('save')
+            ->assertHasErrors('camelFoo.bar', 'required')
+            ->assertSee('The bar field is required.');
+    }
+
     /** @test */
     public function validate_message_still_honors_original_custom_attributes_if_property_is_a_model()
     {
@@ -195,10 +206,40 @@ class Foo extends Model
     }
 }
 
+class CamelFoo extends Model
+{
+    use Sushi;
+
+    protected function getRows()
+    {
+        return [[
+            'bar' => 'baz'
+        ]];
+    }
+}
+
+class ComponentWithCamelCasedModelProperty extends Component
+{
+    public $camelFoo;
+
+    protected $rules = [
+        'camelFoo.bar' => 'required'
+    ];
+
+    public function save()
+    {
+        $this->validate();
+    }
+
+    public function render()
+    {
+        return view('dump-errors');
+    }
+}
+
 class ComponentForEloquentModelHydrationMiddleware extends Component
 {
     public $foo;
-
     protected $rules = [
         'foo.bar' => 'required',
         'foo.bar_baz' => 'required',
@@ -218,7 +259,7 @@ class ComponentForEloquentModelHydrationMiddleware extends Component
 
     public function performValidateOnly($field)
     {
-       $this->validateOnly($field);
+        $this->validateOnly($field);
     }
 
     public function render()