jasonb's avatar

Error: Method name must be a string

I am going through Laravel from scratch using Laravel 8. on Episode 33 - Attach and Validate Many-to-Many Inserts.

Have have added the tags array in my blade view and No issues there. This is a multi select so I named the select field tags[] When I submit my form I get error: "Method name must be a string" Showing line 75 in my ArticleController

$article = new Article($this->$validatedAttributes());

ArticleController store function

 public function store(){
     //   dd(request()->all());

        // validate request and store as $validatedAttributes for mass insertion.
       $validatedAttributes = request()->validate([
           'title' => 'required',
            'excerpt' => 'required',
            'body' => 'required',
           'tags' => 'required|array'
        ]);
        // Persist the new resource/Article
            //Article::create($validatedAttributes);
        $article = new Article($this->$validatedAttributes());
        $article->user_id = 1;  // auth()->id()
        $article->save();

        $article->tags()->attach(request('tags'));

        return redirect('/articles');

    }

My Model


namespace App\Models;
use App\User;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;
    protected $fillable = ['title', 'excerpt', 'body', 'tags'];
    // protected $guarded = []; // turns off the need for protected $fillable for mass assignment.

    public function  author()
    {
        // an ar
        return $this->belongsTo(User::class, 'user_id');
    }


    // an article has many tags, tags can have many articles

    public function tags(){
        return $this->belongsToMany(tag::class);
    }
}

What am I missing? I would be grateful for any assistance.

0 likes
20 replies
tykus's avatar
new Article($validatedAttributes);

$validatedAttributes is a variable, not a method, and it is in the same scope so there is not $this

1 like
jasonb's avatar

With that change I get this error now: ErrorException Array to string conversion Illuminate\Foundation\Bootstrap\HandleExceptions::handleError vendor/laravel/framework/src/Illuminate/Support/Str.php:494

     $result .= (array_shift($replace) ?? $search).$segment;
tykus's avatar

Where is the rest of that code - need context?

jasonb's avatar

This is Built into Laravel:

  public static function replaceArray($search, array $replace, $subject)
    {
        $segments = explode($search, $subject);

        $result = array_shift($segments);

        foreach ($segments as $segment) {
            $result .= (array_shift($replace) ?? $search).$segment;
        }

        return $result;
    }
jasonb's avatar

Illuminate\Foundation\Bootstrap\HandleExceptions::handleError vendor/laravel/framework/src/Illuminate/Support/Str.php:494

tykus's avatar

Ok, but clearly your application code is calling (or a Class/method that ultimately calls) that method somewhere; why is it getting an array instead of a string? Did you do something with the Article model constructor?

jasonb's avatar

I have not changed any of the Laravel Code other than adding the Model, Controller and the view.

So here is the complete code.:

Controller

<?php

namespace App\Http\Controllers;
use App\Models\Article;
use App\Models\Tag;
use Illuminate\Http\Request;
use App;

class ArticlesController extends Controller
{
  // Lets authenticate
    public function __construct()
    {
        $this->middleware('auth');
    }
// dd(Auth::user()->name);
    // show all articles

    public function list(){

if (request('tag')) {
    $articles = Tag::where('name', request('tag'))->firstOrFail()->articles;
    return view('/articles/latest', ['articles' => $articles]);

}

else {
   $articles = Article::take(100)->latest()->get();
    return view('/articles/latest', ['articles' => $articles]);
}
//
//
//        return view('/articles/latest',[
//            'articles' => \App\Models\Article::take(6)->latest()->get()
//        ]);



    }
    // show single Article
    public function show(Article $article)
    {
        //dd($article->title);
        return view('articles.show', ['article' => $article]);
    }

    public function index(){
        return view('/articles/list',[
            // this will return a collection of all articles limit 100.
            'articles' => \App\Models\Article::take(100)->latest()->get()
        ]);
    }

    public function create(){
        //show a view to create a new resource

        return view('articles.create', [
            'tags' => Tag::all()
        ]);

    }

    public function store(){
     //   dd(request()->all());

        // validate request and store as $validatedAttributes for mass insertion.
       $validatedAttributes = request()->validate([
           'title' => 'required',
            'excerpt' => 'required',
            'body' => 'required',
           'tags' => 'required|array'
        ]);
        // Persist the new resource/Article
            //Article::create($validatedAttributes);
        $article = new Article($validatedAttributes);
        $article->user_id = '1';  // auth()->id()
        $article->save();

        $article->tags()->attach(request('tags'));

        return redirect('/articles');

    }



    public function edit(Article $article){
        // Show a view to edit an existing resource
        // Find the article associated with the id


         return view('articles.edit', ['article' => $article]);

    }

    public function update(Article $article){
        // inlined the request. Validates and updates in one shot.
        $article->update(request()->validate([
            'title' => 'required',
            'excerpt' => 'required',
            'body' => 'required'
        ]));

        // Persist the edited resource
      //  $article = Article::findorfail($id);
//        $article->title = request('title'); // this is the long way.
//        $article->excerpt = request('excerpt');
//        $article->body = request('body');
//        $article->save();


        return redirect('/articles/' . $article->id);



    }

    public function destroy($id){
        // Delete a resource
        $article = Article::findorfail($id);
        $article->delete();
        return redirect('/articles/all');

    }


}

Model

<?php

namespace App\Models;
use App\User;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Article extends Model
{
    use HasFactory;
    protected $fillable = ['title', 'excerpt', 'body', 'tags' , 'user_id'];
    // protected $guarded = []; // turns off the need for protected $fillable for mass assignment.

    public function  author()
    {
        // an ar
        return $this->belongsTo(User::class, 'user_id');
    }


    // an article has many tags, tags can have many articles

    public function tags(){
        return $this->belongsToMany(tag::class);
    }
}

View:

  <div class="form-group row">
                            <label class="col-md-2 col-form-label">Select</label>
                            <div class="col-md-10">
                                <select
                                    class="form-control @error('tags') is-invalid @enderror {{old('tags') ? 'is-valid' : ''}}"
                                    name="tags[]"
                                    multiple
                                >

                                    @foreach($tags as $tag)
                                        <option value="{{ $tag->id }}">{{ $tag->name }}</option>
                                    @endforeach
                                </select>
                                @if($errors->has('tags'))
                                    <p class="text-danger" role="alert">
                                        {{ $errors->first('tags') }}
                                    </p>
                                @endif
                            </div>
                        </div>

Route

Route::get('/articles/create', 'ArticlesController@create' ); // Create new Article
Route::post('/articles', 'ArticlesController@store' ); // C// Persist the new article
tykus's avatar

What is this for?

{{old('tags') ? 'is-valid' : ''}}

And you’re getting the exception whenever you submit the form? Maybe post the full stack trace here

jasonb's avatar

That is blade token that would set the values of the select if the page returned with errors, maintains the value if another for field is blank.

jasonb's avatar

Pulling this out I am still getting the same error. So Maybe an non issue there?

tykus's avatar

set the values of the select if the page returned with errors

It does not.

Anyway, it was a curiosity rather than a candidate issue. Post the stacktrace from the exception so we can see where the offending value comes from.

Ishra's avatar

This is your original code:

// validate request and store as $validatedAttributes for mass insertion.
$validatedAttributes = request()->validate([
	'title' => 'required',
	'excerpt' => 'required',
	'body' => 'required',
	'tags' => 'required|array'
]);

// Persist the new resource/Article
//Article::create($validatedAttributes);
$article = new Article($this->$validatedAttributes());
$article->user_id = 1;  // auth()->id()
$article->save();

$article->tags()->attach(request('tags'));

I have a few notes:

  • for mass insertion. is not what you are doing here, you are doing mass assignment (just so you use the proper term if you search for this later)
  • the original error as pointed out earlier was that you used $validatedAttributes as a method, correct should be: $article = new Article($validatedAttributes);
  • the second error you are getting Array to string conversion is because $validatedAttributes contains tags which is an array, this should work: $article = new Article(Arr::except($validatedAttributes, 'tags')); - in short, remove the tags from validatedAttributes before creating the new Article.

https://laravel.com/docs/7.x/helpers#method-array-except

i have a bonus suggestion also from looking at your code, since you already are using the request, you can fetch the user from the request also instead of using auth()->id(): as this: request()->user()->id

even better, typehint the request in your controller method

public function store(Request $request)
{
    $validatedAttributes = $request->validate([

    // ...

    $article->user_id = $request->user()->id;
1 like
jasonb's avatar

@ishra This produces: Error Class 'App\Http\Controllers\Arr' not found

jasonb's avatar

Being that I am using Laravel 8 does that work the same?

Ishra's avatar

You need to import Arr also.

Remember to include this at the top with other use statements.

use Illuminate\Support\Arr;

Yes, this works from atleast Laravel 5 (or even older) to Laravel 8.

jasonb's avatar

OK that is added but getting : Error Function name must be a string

   $article = new Article(Arr::except($validatedAttributes(), 'tags'));
Ishra's avatar
Ishra
Best Answer
Level 6

Sorry a small error in my example, but remember that $validatedAttributes is a variable and not a method, it should be like this:

$article = new Article(Arr::except($validatedAttributes, 'tags'));

note the difference from before, i removed the () after $validatedAttributes now.

2 likes
jasonb's avatar

OH!!!!! That did it! Whew!!! Thank you so much!

1 like
cryptlava's avatar

i'm faced with the same above error

i haven't made any changes in a while and i cant say for sure where the isseue is

$sendSms->$gateway($user->mobile,$general->sitename,$message,$general->sms_config);

Please or to participate in this conversation.