Symfony RedirectToRoute with array paramater - php

I'm working on an upload system based on Symfony 4 and PHPSpreadsheet.
My user uploads the excel file. I then create the products/categories... At the end I want to get all categories created. The following Doctrine query :
/**
* #param $user_id
* #return array
* #throws \Doctrine\ORM\NonUniqueResultException
*/
public function checkIfNew($user_id): ?array {
return $this->createQueryBuilder('c')
->andWhere('c.categorie_parent_id is NULL')
->andWhere('c.created_by = :val')
->setParameter('val', $user_id)
->getQuery()
->getResult()
;
}
gets all my categories where created_by is by the user ID, and where Parent is null.
What I want to do is to get an array of all those categories and then redirect the user to a rendered Twig template page where he can make the link.
But I don't really understand how to use the parameters...
In my Upload I've set this :
$isNew = $this->getDoctrine()
->getRepository(Categorie::class)
->checkIfNew($this->getUser()->getId());
if (!is_null($isNew)){
$this->addFlash('success', 'Catalogue crée avec succès');
return $this->redirectToRoute('admin.categorie.link', $this->getUser()->getId());
}
I don't understand how I can use the request to redirect the user correctly using the route :
/**
* #Route("/admin/categories/import", name="admin.categorie.link", methods={"GET|POST"})
* #param Categorie $categories
*/
public function linkImport()
{
// What TODO here ?
return $this->render('admin/categories/link.html.twig', compact('categories'));
}
Well I suppose I have to add $isNew as a parameter for my request ? But did I reuse the array after to use this in my twig, and display the element from this array inside my twig.

There is a small error:
If your route is "/admin/categories/import" and you want to transfer a value like "$this->getUser()->getId()" then this should be like
Route "/admin/categories/import/{userId}" and your return would be:
return $this->redirectToRoute('admin.categorie.link', ['userId' => $this->getUser()->getId()]);
and your controller could take the userId as var:
/**
* #Route("/admin/categories/import/{userId}", name="admin.categorie.link", methods={"GET|POST"})
*/
public function linkImport(int $userId, EntityManagerInterface $em) // var name must be the same as in the route
{
// What TODO here ?
$categories = $em->getRepository(Categories::class)->findByUserId($userId);
return $this->render('admin/categories/link.html.twig', ['categories' => $categories]);
}
Now you can access your categories in twig with {{categories}}

Related

Laravel Form best way to store polymorphic relationship

I have a notes model. Which has a polymorphic 'noteable' method that ideally anything can use. Probably up to 5 different models such as Customers, Staff, Users etc can use.
I'm looking for the best possible solution for creating the note against these, as dynamically as possible.
At the moment, i'm adding on a query string in the routes. I.e. when viewing a customer there's an "Add Note" button like so:
route('note.create', ['customer_id' => $customer->id])
In my form then i'm checking for any query string's and adding them to the post request (in VueJS) which works.
Then in my controller i'm checking for each possible query string i.e.:
if($request->has('individual_id'))
{
$individual = Individual::findOrFail($request->individual_id_id);
// store against individual
// return note
}elseif($request->has('customer_id'))
{
$customer = Customer::findOrFail($request->customer_id);
// store against the customer
// return note
}
I'm pretty sure this is not the best way to do this. But, i cannot think of another way at the moment.
I'm sure someone else has come across this in the past too!
Thank you
In order to optimize your code, dont add too many if else in your code, say for example if you have tons of polymorphic relationship then will you add tons of if else ? will you ?,it will rapidly increase your code base.
Try instead the follwing tip.
when making a call to backend do a maping e.g
$identifier_map = [1,2,3,4];
// 1 for Customer
// 2 for Staff
// 3 for Users
// 4 for Individual
and so on
then make call to note controller with noteable_id and noteable_identifier
route('note.create', ['noteable_id' => $id, 'noteable_identifier' => $identifier_map[0]])
then on backend in your controller you can do something like
if($request->has('noteable_id') && $request->has('noteable_identifier'))
{
$noteables = [ 'Customers', 'Staff', 'Users','Individual']; // mapper for models,add more models.
$noteable_model = app('App\\'.$noteables[$request->noteable_identifier]);
$noteable_model::findOrFail($request->noteable_id);
}
so with these lines of code your can handle tons of polymorphic relationship.
Not sure about the best way but I have a similar scenario to yours and this is the code that I use.
my form actions looks like this
action="{{ route('notes.store', ['model' => 'Customer', 'id' => $customer->id]) }}"
action="{{ route('notes.store', ['model' => 'User', 'id' => $user->id]) }}"
etc..
And my controller looks this
public function store(Request $request)
{
// Build up the model string
$model = '\App\Models\\'.$request->model;
// Get the requester id
$id = $request->id;
if ($id) {
// get the parent
$parent = $model::find($id);
// validate the data and create the note
$parent->notes()->create($this->validatedData());
// redirect back to the requester
return Redirect::back()->withErrors(['msg', 'message']);
} else {
// validate the data and create the note without parent association
Note::create($this->validatedData());
// Redirect to index view
return redirect()->route('notes.index');
}
}
protected function validatedData()
{
// validate form fields
return request()->validate([
'name' => 'required|string',
'body' => 'required|min:3',
]);
}
The scenario as I understand is:
-You submit noteable_id from the create-form
-You want to remove if statements on the store function.
You could do that by sending another key in the request FROM the create_form "noteable_type". So, your store route will be
route('note.store',['noteableClass'=>'App\User','id'=>$user->id])
And on the Notes Controller:
public function store(Request $request)
{
return Note::storeData($request->noteable_type,$request->id);
}
Your Note model will look like this:
class Note extends Model
{
public function noteable()
{
return $this->morphTo();
}
public static function storeData($noteableClass,$id){
$noteableObject = $noteableClass::find($id);
$noteableObject->notes()->create([
'note' => 'test note'
]);
return $noteableObject->notes;
}
}
This works for get method on store. For post, form submission will work.
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Requests\NoteStoreRequest $request
* #return \Illuminate\Http\Response
*/
public function store(NoteStoreRequest $request) {
// REF: NoteStoreRequest does the validation
// TODO: Customize this suffix on your own
$suffix = '_id';
/**
* Resolve model class name.
*
* #param string $name
* #return string
*/
function modelNameResolver(string $name) {
// TODO: Customize this function on your own
return 'App\\Models\\'.Str::ucfirst($name);
}
foreach ($request->all() as $key => $value) {
if (Str::endsWith($key, $suffix)) {
$class = modelNameResolver(Str::beforeLast($key, $suffix));
$noteable = $class::findOrFail($value);
return $noteable->notes()->create($request->validated());
}
}
// TODO: Customize this exception response
throw new InternalServerException;
}

Symfony 4 - populate array with the users of type ROLE_FITTER

Symfony 4 app using FOSUserBundle.
Trying to list users with a particular role (ROLE_FITTER) in a dropdown menu using jquery/ajax.
I'm trying to add an Action in my APIController that will get the list of users with role ROLE_FITTER and return a JSON array with them in it - so can then populate the dropdown with list of these users.
I have tried to pull together some different examples, but not sure how to correctly build the query:
namespace App\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
class APIController extends AbstractController
{
/**
* Returns a JSON string of the Fitters with their id.
*
* #Route(/profile/booking/new/fitters)
* #param Request $request
* #return JsonResponse
*/
public function listFitters(Request $request)
{
// Get Entity manager and repository
$em= $this->getDoctrine()->getManager();
$qb = $em->createQueryBuilder();
$qb->select('u')
->from('userBundle:User', 'u')
->where('u.id = :user')
->andWhere('u.roles LIKE :roles')
->setParameter('user', $id)
->setParameter('roles', '%"' . $role . '"%');
$user = $qb->getQuery()->getResult();
// Serialize into an array the data that we need, in this case only name and id
$responseArray = array();
foreach ($users as $user) {
$responseArray[] = array(
"id" => $user->getId(),
"name" => $user->getName()
);
}
// Return array for dropdown
return new JsonResponse($responseArray);
}
}
How do I populate this array with the users of type ROLE_FITTER?
Well using serialized strings in sql is never a good idea, no idea why such a popular bundle would do that, but it is what it is.
Your query as written checks for a user with specific id, and role. but you never provide the id or role!.
I dont think you want to query by id, so the correct query should be something like this:
public function listFitters(Request $request)
{
// Get Entity manager and repository
$em= $this->getDoctrine()->getManager();
$qb = $em->createQueryBuilder();
//set required role
$role = 'ROLE_FITTER';
$qb->select('u')
->from('userBundle:User', 'u')
->where('u.roles LIKE :roles')
->setParameter('roles', '%"' . $role . '"%');
$user = $qb->getQuery()->getResult();
// Serialize into an array the data that we need, in this case only name and id
$responseArray = array();
foreach ($users as $user) {
$responseArray[] = array(
"id" => $user->getId(),
"name" => $user->getName()
);
}
// Return array for dropdown
return new JsonResponse($responseArray);
}
Probably you should only select the fields you want (id, name) and avoid the array building loop, but i am not particularly familiar with symfony / doctrine so not sure of the correct syntax

Symfony - render view by defined status

I defined status field in my db table like
$newPlay->setStatus(Plays::STATUS_PUBLISHED);
that is now saved in that db table.
I need to render a view with fields that have that status.
I made an api call for that but I keep getting an error
Controller "AppBundle\Website\Controller\PublishedTicketsController::publishedTicketsAction()" requires that you provide a value for the "$status" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
This is my api call..
/**
* #Route("/published-tickets", name="published_tickets")
* #param Request $request
* #return \Symfony\Component\HttpFoundation\Response
* #throws \Exception
*/
public function publishedTicketsAction(Request $request, $status)
{
$query = $this->getDoctrine()
->getRepository('AppBundle:Plays')->findBy(['status' => $status]);
return $this->render('#FrontTemplates/pages/published-tickets.html.twig', array(
'query' => $query,
'status' => $status
));
}
I have done it like this..
In my service..
public function publishedTicket()
{
$query = $this->getPlaysRepository()
->createQueryBuilder('t')
->select('t')
->where('t.status =:status')
->setParameter('status', Plays::STATUS_PUBLISHED)
->getQuery()
->getResult();
return $query;
}
and in my controller
$playPublish = $this->container->get('publish.tickets')->publishedTicket();
return $this->render('#FrontTemplates/pages/published-tickets.html.twig', array(
'playPublish' => $playPublish
));
this is the part I could't figure out..
->setParameter('status', Plays::STATUS_PUBLISHED)

Laravel 5 make routing with unique parameter

I am currently using a route with a parameter nameto query the profile of a user in my application.
So for example: /members/johnwill show the profile of John.
You see the problem here, if there are 2 John's then it's gonna be a problem.
I know I could do something like this /members/idsince id is unique but I want the url to look pretty with the user's name and not a random number.
So my question is if there is a way to use the id to make it unique but to display the name in url?
my route:
Route::get('/members/{name}', 'UserController#usersProfile');
My usersProfile method:
/**
* Returns a users profile.
*
* #param string $name
*
* #return \Illuminate\Http\Response
*/
public function usersProfile($name)
{
$profile = $this->userService->getProfile($name);
if ($profile == null) {
return redirect('members')->with('status', 'Whoops, looks like that member does not exist (yet).');
}
return view('members/profile', ['profile' => $profile]);
}
you can use some package for slug your model name :
https://github.com/spatie/laravel-sluggable
https://github.com/cviebrock/eloquent-sluggable
these packages automatically doing that for you.
if you have same name this will happen:
http://example.com/post/my-dinner-with-andre-francois
http://example.com/post/my-dinner-with-andre-francois-1
http://example.com/post/my-dinner-with-andre-francois-2
just in production i have some issue with scopes...
when you apply global scope on model , it may create duplicate slug for that ... you can fix that by add this to your model :
public function scopeFindSimilarSlugs(Builder $query, Model $model, $attribute, $config, $slug)
{
$separator = $config['separator'];
return $query->withoutGlobalScopes()->where(function (Builder $q) use ($attribute, $slug, $separator) {
$q->where($attribute, '=', $slug)
->orWhere($attribute, 'LIKE', $slug . $separator . '%');
});
}
You should add another column to your user table with name 'slug' or 'username' and map that column in your route.
In your users model, make a static function to generate unique slug with a number attached and save it in that new column to provide users with public/private profile URLs.
This below sample will stop returning slug/username after 100 users with the same name.
public static function getUniqueSlug($name) {
$original_slug = getSlug($name); // Default getter to get value in model
$slug = str_slug($original_slug); //Convert a string into url friendly string with hyphens
while(true) {
$count = User::where('slug', $slug)->count();
if($count > 0) {
$slug = $original_slug."-".rand(1,99);
} else {
return $slug;
}
}
}

How do i pass number to middleware "can" in Laravel Authorization

based on documentation https://laravel.com/docs/5.5/authorization#via-middleware
use App\Post;
Route::put('/post/{post}', function (Post $post) {
// The current user may update the post...
})->middleware('can:update,post');
the 'post' in 'can:update,post' is variable passed from '{post}'.
im trying to use middleware('can:update,1'). its not working. maybe its search for '$1' variable, how to pass number '1' to 'can' middleware?
update this is the gate:
Gate::define('update', function ($user, $role){
$id = $user->id;
$roles = $user::find($id)->roles()->get();
$roleid = $roles[0]->pivot->role_id;
//above code just for get role id, and $role_id is 1
return $roleid === $role;
});
Probably you don't have Policy created for Post.
You can create by command:
php artisan make:policy PostPolicy --model=Post
and then implement method update in that policy.
I to had this same problem so I did some digging into the 'can' middleware (Which maps to Illuminate\Auth\Middleware\Authorize)
Once in the class we see the following code
/**
* Get the model to authorize.
*
* #param \Illuminate\Http\Request $request
* #param string $model
* #return \Illuminate\Database\Eloquent\Model|string
*/
protected function getModel($request, $model)
{
if ($this->isClassName($model)) {
return trim($model);
} else {
return $request->route($model, null) ?:
((preg_match("/^['\"](.*)['\"]$/", trim($model), $matches)) ? $matches[1] : null);
}
}
What this means is...
If our string passed in is a class name then return that class name
If it is not a class name then...
1 Try to get it from the route, then return the route param
2 Try to get the model from the string via the regex "/^['\"](.*)['\"]$/"
So now lets say we have the middleware call of
$this->middleware(sprintf("can:create,%s,%s", User::class, Role::SUPPORT));
This will not work because the Role::SUPPORT does not match the regex
To match it we simply need to place the Role::SUPPORT into quotes.
TAKE NOTE OF THE "'" around the second %s
$this->middleware(sprintf("can:create,%s,'%s'", User::class, Role::SUPPORT));
To answer your question specifically, quote your '1' value
middleware("can:update,'1'").

Categories