PHP Inheritance Override function body - php

Hey Guys I have the following Scenario and I can't think of a better way. Maybe you guys can provide a more DRY method
So update method BaseController from Laravel Voyager
public function update(Request $request, $id)
{
// Update Logic Here
// Redirect Logic
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = redirect()->back();
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
Custom Controller that extends the above Base Controller
public function update(Request $request, $id)
{
// Update Logic Copied and Pasted from Base ( Yuck :( )
// Small Change to the Redirect Logic
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = redirect()->route("voyager.{$dataType->slug}.show",$id);
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
So my question is with the current structure of the Base Controller Is there any other way to override the redirect logic without literally copying and pasting the whole lot of code
I do not want to edit the BaseController as it will stop me from updating the package
Any thoughts would be great
Cheer

Simply use smaller functions to extract that logic and override it, similar approaches with overriding function through inheritance for changing logic, is used by Laravel on Models see getRouteKey() for example.
In your BaseController.php, i would split it up like so.
{
if (auth()->user()->can('browse', app($dataType->model_name))) {
$redirect = redirect()->route("voyager.{$dataType->slug}.index");
} else {
$redirect = $this->browseRedirectLocation();
}
}
return $redirect->with([
'message' => __('voyager::generic.successfully_updated')." {$dataType->getTranslatedAttribute('display_name_singular')}",
'alert-type' => 'success',
]);
}
protected function browseRedirectLocation() {
{
return $redirect = redirect()->back();
}
Now you should be able to override redirect location in your CustomController.php, instead of the whole function in your implementation class. As i could see it was only the redirect that was changed.
protected function browseRedirectLocation() {
{
return redirect()->route("voyager.{$dataType->slug}.show",$id);
}

Related

Laravel restore back deleted record

I'm trying to implement the queue that system will be restored back a deleted record. Now my code is working without error but the record will not restore back after deleted.
public function delete_invoice($job, $data)
{
Debugbar::info("invoiceSale");
try {
return DB::transaction(function ()use ($job,$data) {
});
} catch (TransactionException $e) {
# reestore function
extract($data);
$data = $Class::withTrashed()->find($id);
$data->restore();
Debugbar::info($data->toArray());
return Response::json(['errors' => array_flatten($e->getErrors())], 400);
}
}
This is the function from controller
public function destroy($id, $message = '')
{
Debugbar::info("ok");
Queue::push('IQueue#delete_invoice', [
'id' => $id,
'Class' => $this->Class,
]);
return parent::destroy($id, trans("$this->class.invoice")); <--delete invoice
}
you can use the following code hope it will help you.
public function destroy(Trip $trip)
{
$trip->delete();
flash()->warning('Trip '.$trip->id.' successfully deleted! <a href=trips/'.$trip->id.'/restore>UNDO</a>');
return redirect('trips');
}
public function restore(Request $request)
{
$trip = Trip::withTrashed()->where('id', $request['id'])->restore();
return redirect ('trips');
}
I'm assuming your code deletes the record somewhere else and the code you presented here is supposed to restore that record, based on the model class and record id passed via the $data array as ['Class' => ..., 'id' => ...].
Then what is your transaction meant to do? Is there any code you did not paste in? Otherwise catch is never called as there is no exception thrown and hence you code is not executed.
So just remove the try and catch.

Laravel Custom Validation Method

I'm trying to develop a PHP game with Laravel, and so far a user - with enough gold and not part of a guild - can create a guild using a simple form with one text field. The issue is that currently I'm using Laravel's dd() function in order to show that they failed to have the gold or were already in a guild.
As such, I went looking for a way to give it a more baked-in feel by seeing if I could put this behavior into a custom rule/validator, but I'm unsure as to how to go about this. Examples would be preferred... here's my current function.
public function store(Request $request)
{
$request->validate([
'name' => 'required|min:4|alpha_dash|unique:guilds'
]);
$char = Auth::user()->character;
$cost = config('game.create-guild-cost');
$guild = new Guild;
if($char->gold < $cost) {
dd('Not enough money');
}
if($char->guild != null) {
dd('You cannot already be in a guild.');
}
$guild->name = request('name');
$guild->leader_id = $char->id;
$guild->save();
$char->gold = $char->gold - $cost;
$char->guild_id = $guild->id;
$char->save();
return redirect()->route('guilds.show', ['guild' => $guild]);
}
public function store(Request $request)
{
$validator = Validator::make($request->all(), [
'name' => 'required|min:4|alpha_dash|unique:guilds'
]);
if ($validator->fails()) {
return redirect()
->back() //please double check this but you got the idea
->withErrors($validator)
->withInput();
}
// Do your stuff here....
}
So basically Laravel provides you to put your error messages in session behind the scene and then go to your desired page get the errors from the session and show them nicely in your view files.

Hash password before saving with Laravel Backpacker

A simple question: how do I modify (hash) the request value before saving it with Laravel Backpacker CRUD admin?
As far as i understand, it should be done somewhere before these methods are executed in the crud controller:
public function store(StoreRequest $request)
{
return parent::storeCrud();
}
public function update(UpdateRequest $request)
{
return parent::updateCrud();
}
but I have no idea how to do it correctly.
Edit: the request is not a Request object, but rather StoreRequest or UpdateRequest that looks something like this:
Fix:
public function update(UpdateRequest $request)
{
// Hash password before save
if (!empty($request->password)) {
$request->offsetSet('password', Hash::make($request->password));
}
return parent::updateCrud($request); // <-- Pass the modified request, otherwise the CRUD reads it again from post data
}
You can update $request values using the offsetSet method
$request->offsetSet('name', $newName);
Edit: To update user password you can do something like this:
public function update_password(Request $request)
{
$user = User::find(Auth::user()->id);
if (Hash::check($request->old_password, $user->password)) {
$user->fill([
'password' => Hash::make($request->password)
])->update();
return redirect()->back()->with('message' => 'Your password has been updated.');
}
else {
return redirect()->back()->with('message' => 'The password entered do not match our records.');
}
}
I did not check the code but it should work. Now update it to your needs.
If you're asking about how to modify data in $request variable, you can just do this:
$request->property = 'New value';
Also, you can add data to reuqest itself (not into variable):
request()->request->add(['key' => 'value']);

Slim PHP: Only catch valid routes with middleware

I'm writing a REST API with Slim. I have written a small middleware to protect the resources so only authenticated users will be able to access them:
<?php
class SecurityMiddleware extends \Slim\Middleware
{
protected $resource;
public function __construct($resource)
{
$this->resource = $resource;
}
public function call()
{
//get a reference to application
$app = $this->app;
//skip routes that are exceptionally allowed without an access token:
$publicRoutes = ["/","/login","/about"];
if (in_array($app->request()->getPathInfo(),publicRoutes)){
$this->next->call(); //let go
} else {
//Validate:
if ($this->resource->isValid()){
$this->next->call(); //validation passed, let go
} else {
$app->response->setStatus('403'); //validation failed
$app->response->body(json_encode(array("Error"=>"Access token problem")));
return;
}
}
}
}
This works, but the undesired side effect is the middleware does not make a distinction between existing routes and non-existing routes. For example, if a the user attempts to request a route like /dfghdfgh which does not exist, instead of getting an HTTP status code of 404 he'll get a 403 saying there is no access token. I would like to add an implementation similar to the following check on the middleware class:
if ($app->hasRoute($app->request->getPathInfo()){
$this->next->call(); //let go so user gets 404 from the app.
}
Any ideas how this can be achieved?
I use a hook to do what you're trying to do, as MamaWalter suggested, but you want to use slim.before.dispatch rather than an earlier hook. If the route your user is trying to visit doesn't exist, the hook will never be called and the 404 gets thrown.
I'm doing exactly that in my own Authorization Middleware. Works like a charm.
Maybe my implementation will work for you:
<?php
class CustomAuth extends \Slim\Middleware {
public function hasRoute() {
$dispatched = false;
// copied from Slim::call():1312
$matchedRoutes = $this->app->router->getMatchedRoutes($this->app->request->getMethod(), $this->app->request->getResourceUri());
foreach ($matchedRoutes as $route) {
try {
$this->app->applyHook('slim.before.dispatch');
$dispatched = $route->dispatch();
$this->app->applyHook('slim.after.dispatch');
if ($dispatched) {
break;
}
} catch (\Slim\Exception\Pass $e) {
continue;
}
}
return $dispatched;
}
public function call() {
if ($this->hasRoute()) {
if ($authorized) {
$this->next->call();
}
else {
$this->permissionDenied();
}
}
else {
$this->next->call();
}
}
}
Not exactly what you asking for, but personnaly when i need to check authentification on some routes i do it like this.
config:
$config = array(
...,
'user.secured.urls' => array(
array('path' => '/user'),
array('path' => '/user/'),
array('path' => '/user/.+'),
array('path' => '/api/user/.+')
),
...
);
middleware:
/**
* Uses 'slim.before.router' to check for authentication when visitor attempts
* to access a secured URI.
*/
public function call()
{
$app = $this->app;
$req = $app->request();
$auth = $this->auth;
$config = $this->config;
$checkAuth = function () use ($app, $auth, $req, $config) {
// User restriction
$userSecuredUrls = isset($config['user.secured.urls']) ? $config['user.secured.urls'] : array();
foreach ($userSecuredUrls as $url) {
$urlPattern = '#^' . $url['path'] . '$#';
if (preg_match($urlPattern, $req->getPathInfo()) === 1 && $auth->hasIdentity() === false) {
$errorData = array('status' => 401,'error' => 'Permission Denied');
$app->render('error.php', $errorData, 401);
$app->stop();
}
}
};
$app->hook('slim.before.router', $checkAuth);
$this->next->call();
}
but if almost all your routes need authentification maybe not the best solution.
great example: http://www.slideshare.net/jeremykendall/keeping-it-small-slim-php

How can I redirect to the same url?

I'm trying to figure out how to make redirect to the same url after processing form in silex:
public function someAction(Application $app)
{
$form = ... // building form
if ('POST' === $app['request']->getMethod()) {
$form->bindRequest($app['request']);
if ($form->isValid())
{
$url = $app['url_generator']->generate(
$app['request']->get('_route'),
$app['request']->get('_route_params')
);
return $app->redirect($url);
}
}
return $app['twig']->render(
'form.html.twig',
array(
'form' => $form->createView()
)
);
}
It's possible in Symfony, but it's not working here. (Of course, i can always redirect to something like $url?success)
UPD: There's everything correct with $url. The point is that if you are trying to redirect to exactly the same url, it won't work.
The Request class has a getRequestUri() method. You can use that like this:
return $app->redirect($request->getRequestUri());
Sorry to answer your question with another question, but why would you want to redirect to the same page? The logic for your route should simply display your view after processing the form.
public function someAction(Application $app)
{
$form = ... // building form
if ('POST' === $app['request']->getMethod()) {
$form->bindRequest($app['request']);
if ($form->isValid())
{
$url = $app['url_generator']->generate(
$app['request']->get('_route'),
$app['request']->get('_route_params')
);
//return $app->redirect($url);
// just remove the return here and you're all set!
}
}
return $app['twig']->render(
'form.html.twig',
array(
'form' => $form->createView()
)
);
}

Categories