How to fix problem with _construct function after Route Model Binding? - php

After setting up a controller and a view to show a specific entry from my database, I wanted to use laravels function of Route Model Binding to fetch the data fromn the database and pass it to the view. However I am getting following error:
Symfony\Component\Debug\Exception\FatalThrowableError thrown with
message "Argument 2 passed to
Symfony\Component\HttpFoundation\RedirectResponse::__construct() must
be of the type integer, array given, called in
C:\xampp\htdocs\laravel\Cyberchess\vendor\laravel\framework\src\Illuminate\Routing\Redirector.php
on line 203"
I've tried to add this line to TrustProxy:
protected $headers = Request::HEADER_X_FORWARDED_ALL;
as the internet recommended, but when I opened the file, I realised it was already in the code.
My create/store works properly, which is why I assume it has something to do with Route Model Binding. I'm currently using a getRouteKeyName() to change the Key to 'AccID' so it should work.
//my controller
public function show(account $account){
//$account = account::where(['AccID' => $account])->get();
//dd($account);
return redirect('user.show', compact('account'));
}
//my model
class account extends Model
{
public function getRouteKeyName() {
return 'AccID';
}
public $timestamps = false;
}
//my view
<h1 class="title">Your Profile</h1>
<p>{{ $account->Nick }}</p>
I expected it to work just fine(duh), but got said error. When I dd(); the data, it has the information I want inside #attributes and in #original.
If if comment the dd() and let the return do it's work, I get the error.

The redirect() helper function is used to send a redirect 301 response from the server, what you want instead is to return a view like so
public function show(account $account)
{
$account = account::where(['AccID' => $account])->get();
return view('user.show', compact('account'));
}

Related

Get request from controller in model - Laravel

In my API I used "with" method to get parent's model relation and everything works fine.
I want to add an attribute in my relation and return it in my API but I should use request in my model.
Something like this :
Book.php
protected $appends = ['userState'];
public function getUserStateAttribute () {
return User::find($request->id); //request not exists here currently
}
I have $request in my controller (api controller)
Controller.php
public function get(Request $request) {
Post::with('books')->all();
}
I believe using static content to append in array of model is so easy but how about using request's based content ?
I guess you can use request() helper :
public function getUserStateAttribute () {
return User::find(request()->get('id'));
}
Sure this is not really MVC pattern, but it can work
You want to take request as a parameter here:
public function getUserStateAttribute (Request $request) {
return User::find($request->id);
}
That way you can access it in the function. You will just need to always pass the Request object whenever you call that function.
e.g. $book->getUserStateAttribute($request);
Alternatively, you could just pass the ID, that way you need not always pass a request, like so:
public function getUserStateAttribute ($id) {
return User::find($id);
}
Which you would call like:
e.g. $book->getUserStateAttribute($request->id);

Laravel: How do I submit a form and stay in the same view?

I have in Laravel a view in which already foreach loops have been incorporated. At the bottom of the page is a small form. Now I want to send this form to save the data into the database.
At the same time I want to stay in the same view. If I enter the same page in the form, I get an error message. If I want to go back to the view via the controller, I also get an error.
In this error, the output of the data in the loop that was previously passed by another controller no longer works. - What can I do?
Undefined variable: data (View: /srv/users/smartfinance/apps/smartfinance/public/laravel/resources/views/home.blade.php)
<?php $__currentLoopData = $data; $__env->addLoop($__currentLoopData); foreach($__currentLoopData as $d): $__env->incrementLoopIndices(); $loop = $__env->getLastLoop(); ?>
I hope you understand my problem and can give me a hint how to solve it.
These are the two controller:
class checkDataController extends Controller
{
public function data()
{
$data = DB::table('test')->where('booking_date', '=', $today)->get();
return view('home', compact("data"));
}
}
class AddContractController extends Controller
{
public function addNewData(Request $request)
{
$bez = $request->bez;
return view('home');
}
}
Typically this is done by redirecting back to the same page you submitted from. Here is what that would look like in your controller:
public function addNewData(Request $request)
{
$bez = $request->bez;
return back();
}
For a better user experience, you should also add a message to the view with the form:
#if(session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
#endif
And make one small change to your controller:
return back()->with('status', "Successfully submitted {$bez}!");
Your mistake is that you try to display the same view from two different controller methods. Normally, a view is only being used by one controller method. Other methods can redirect to this method in order to (re-)display the same view. This way, the logic of retrieving data for the view is only required in one place.
class CheckDataController extends Controller
{
// route 'check.data'
public function data()
{
$data = DB::table('test')->where('booking_date', '=', $today)->get();
return view('home', compact("data"));
}
}
class AddContractController extends Controller
{
// route 'contract.create'
public function addNewData(Request $request)
{
Contract::create($request->input()); // unsafe, only for demonstration...
return redirect()->route('check.data');
}
}
As you can see, instead of return view('home'), we are returning redirect()->route('check.data'). This will redirect the user to the other controller with the defined route. Of course this means you are actually executing two controller methods within one user action, but that's common practice.
More information about redirects can be found in the official documentation.
An advise for you my friend don't return a view from a post method.
I suppose the addNewData() method is a method that comes from the post route. so when you return a view after a post method you don't provide the data for your page. so laravel throws an error and complains about the missing variable. so what you must do is redirect to the route that views the page. So your method would look something like this:
public function addNewData(Request $request)
{
$bez = $request->bez;
return redirect('YOUR ROUTE TO VIEW THE PAGE (URL)');
}

Laravel attach returns undefined method for belongsToMany relationship

I would like to create a question which has many surveys. In the questions Model:
public function surveys()
{
return $this->belongsToMany(Survey::class, 'survey__surveyquestions');
}
And in the controller when saving a new question:
private $questions;
public function __construct(QuestionsRepository $questions)
{
parent::__construct();
$this->questions = $questions;
}
public function store(Request $request)
{
$this->questions->create($request->all());
$this->questions->surveys()->attach($request->surveys);
return redirect()->route('admin.survey.questions.index')
->withSuccess(trans('core::core.messages.resource created', ['name' => trans('survey::questions.title.questions')]));
}
But I get the following error when it gets to the attach line:
(1/1) FatalErrorException Call to undefined method
Modules\Survey\Repositories\Eloquent\EloquentQuestionsRepository::surveys()
I notice the error mentions EloquentQuestionsRepository but I have added no methods in there so it's just an empty class:
class EloquentQuestionsRepository extends EloquentBaseRepository implements QuestionsRepository
{
}
QuestionRepository:
interface QuestionsRepository extends BaseRepository
{
}
As explained in the response to the main post - the constructor resolves the QuestionsRepository to instance of EloquentQuestionsRepository, which by the look of it is not what the store method needs.
What I would probably do is to make call to create method directly on the model and remove constructor all together - that is unless you need the instance of QuestionsRepository anywhere else in your controller:
public function store(Request $request)
{
$question = Question::create($request->all());
$question->surveys()->attach($request->surveys);
...
}
Also - I'm not sure passing $request->all() is the best thing to do - I'd probably use $request->only(...) or $request->all(...) specifying which items you want to get from the request rather than passing everything from the request to the create method.
On the other note - you could also use Form Request, which would validate data for your before passing it to the store method.
https://laravel.com/docs/5.5/validation#form-request-validation

Laravel 5 form request validation with ID - show

I am wanting to validate a resource controller in Laravel, so that the user can only access clinic.show if they're the owner of said clinic.
I correctly validated this using the following:
public function show($id)
{
if (Clinic::where('id', $id)->where('user_id', Auth::id())->exists()) {
return View::make('clinic.show', ['clinic' => Clinic::where('id', $id)
->first()]);
} else {
abort(401, 'Unauthorized action.');
}
}
However, I believe this is bad practice and I should be using the Form Request feature within Laravel.
I have created a ShowClinicFormRequest.php and added the following code:
public function authorize()
{
$clinicId = $this->route('clinic');
return Clinic::where('id', $clinicId)
->where('user_id', Auth::id())
->exists();
}
And within the show function -
public function show($id, ShowClinicFormRequest $request)
{
return View::make('clinic.show', ['clinic' => Clinic::where('id', $id)->first()]);
}
However, the error that I am getting is:
ReflectionException in RouteDependencyResolverTrait.php line 53: Class
App\Http\Controllers\ShowClinicFormRequest does not exist
It obviously doesn't exist within that directory because it isn't a controller.
Any help would be greatly appreciated.
I believe your form request is located in the App\Http\Requests namespace so you need to import the class or use explicitly:
use App\Http\Requests\ShowClinicFormRequest;
or just
public function show($id, \App\Http\Requests\ShowClinicFormRequest $request)
{}
You might also to take a look at filters or middlewares.

Mocking in controller tests with Laravel model binding

I'm using model binding within my routes to pass models into my controller actions and would like to be able to write tests. It would be preferable if it wasn't required for the test to hit the database.
The model is bound using the username in this example, and then used in the definition of the routes.
// routes.php
Route::model('user', function($value, $route)
{
return User::whereUsername($value)->firstOrFail();
});
Route::get('users/{user}', 'UsersController#show');
In my controller the bound user is passed to the action.
// UsersController.php
function show(User $user)
{
return View::make('users.show', compact('user');
}
Now, in my tests I'm attempting to mock the User.
// UsersControllerTest.php
public function setUp()
{
parent::setUp();
$this->mock = Mockery::mock('Eloquent', 'User');
$this->app->instance('User', $this->mock);
}
public function testShowPage()
{
$this->mock->shouldReceive('whereSlug')->once()->andReturn($this->mock);
$this->action('GET', 'UsersController#show');
$this->assertResponseOk();
$this->assertViewHas('user');
}
When running this test, I get the following error:
ErrorException: Argument 1 passed to UsersController::show() must be an instance of User, instance of Illuminate\Database\Eloquent\Builder given
I'd also like to be able to use return User::firstByAttribtues($value); but Mockery won't let me mock a protected method - is there any way I can get around this?
I had to dig thru Mockery's source code to find this, but have you looked at shouldAllowMockingProtectedMethods ?
Ie, to mock class foo and allow protected methods to be mocked:
$bar = \Mockery::mock('foo')->shouldAllowMockingProtectedMethods();
// now set your expectations up
and then keep going from there.
Not sure why you're not getting an error like unexpected method "firstOrFail" called. But, at first glance, I think the problem is that your model route defined in routes.php is also calling the firstOrFail method.
So, your test should look something like this:
public function testShowPage()
{
$stubQuery = \Mockery::mock('Illuminate\Database\Eloquent\Builder');
$this->mock->shouldReceive('whereSlug')->once()->andReturn($stubQuery);
$stubQuery->shouldReceive('firstOrFail')->andReturn($this->mock);
$this->action('GET', 'UsersController#show');
$this->assertResponseOk();
$this->assertViewHas('user');
}

Categories