Hello i saw a lot of tutorials and still dont know how can i make custom functions in my already done repository.
this is content of my CommentController:
public function newAction(Request $request, $productId)
{
$comment = new Comment();
$form = $this->formFactory->create(CommentForm::class, $comment);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->get('doctrine')->getManager();
/** #var Product $product */
$product = $em->getRepository(Product::class)->find($productId);
$comment->setAuthor($this->tokenStorsge->getToken()->getUser());
$comment->setProduct($product);
$comment->setContent($form->get('content')->getData());
$this->em->persist($comment);
$this->em->flush();
}
return $this->render(':form:comment.html.twig', array(
'form' => $form->createView(),
));
}
and i just want to make some function to make controller more beautiful any ideas? if you give me and example how can i insert my data into database via custom repository function. I know how to make custom query thats all. Every help/idea is very helpfull!
From here
Doctrine 2 ORM does not support INSERT via DQL or the DQL query builder. For a complete syntax, check the EBNF of DQL.
You can add more abstractions if you want your controller to look slightly more beautiful, off the top of my head (effective in Symfony 2.8):
BaseController.php:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
abstract class BaseController extends Controller
{
/**
* #return ProductRepository
*/
protected function getProductRepo()
{
return $this->get('doctrine.orm.entity_manager')->getRepository(Product::class);
}
}
CommentController.php:
Use native controller functions to create a form and get the currently logged in user. And if you are not intend to add more complex logic between $form->isSubmitted() and $form->isValid() use just $form->isValid()
class CommentController extends BaseController
{
public function newAction(Request $request, $productId)
{
$comment = new Comment();
$form = $this->createForm(CommentForm::class, $comment);
$form->handleRequest($request);
if ($form->isValid()) {
/** #var Product $product */
$product = $this->getProductRepo()->find($productId);
$comment->setAuthor($this->getUser());
$comment->setProduct($product);
$comment->setContent($form->get('content')->getData());
$this->em->persist($comment);
$this->em->flush();
}
return $this->render(':form:comment.html.twig', array(
'form' => $form->createView(),
));
}
}
Related
I get the error Controller not found: service "AppBundle / Controller / TestController.php" does not exist
but i don't know how to debug it. Can someone please help me !?
Is it possible that the error comes from the routing or it is something else !?
I just want to clarify that I use Symfony 3.4.
Here is the extract from the TestController.php code
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Task;
use AppBundle\Form\TaskType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
/**
* #Route("test/form-test")
*/
class TestController extends Controller
{
/**
* #Route("/", name="test")
*/
public function newAction(Request $request)
{
// creates a task and gives it some dummy data for this example
$task = new Task();
$task->setTask('Write a blog post');
$task->setDueDate(new \DateTime('tomorrow'));
$form = $this->get('form.factory')->createNamed('addTask', TaskType::class, $task);
$form->handleRequest($request);
$validator = $this->get('validator');
$errors = $validator->validate($form);
if (count($editErrors) > 0) {
$errorsString = (string)$editErrors;
return new Response($errorsString);
}
echo('------------------------------------------------------------------------' . $form->isSubmitted() . ' && ' . $form->isValid());
if ($form->isSubmitted() && $form->isValid()) {
// $form->getData() holds the submitted values
// but, the original `$task` variable has also been updated
$task = $form->getData();
// ... perform some action, such as saving the task to the database
// for example, if Task is a Doctrine entity, save it!
// $entityManager = $this->getDoctrine()->getManager();
// $entityManager->persist($task);
// $entityManager->flush();
return new Response('<h1>-----------------------------------------------------------------------------------OK</h1>');
}
return $this->render('default/index.html.twig', [
'form' => $form->createView(),
'errors' => $errors,
]);
}
}
thank you in advance
I want to use a single controller to save my comments for multiple models. So I created the CommentController, with the following store method:
public function store(Teacher $teacher, Request $request)
{
$input = $request->all();
$comment = new Comment();
$comment->user_id = Auth::user()->id;
$comment->body = $input['body'];
$teacher->comments()->save($comment);
return redirect()->back();
}
In my view, I have:
{!! Form::open([
'route' => ['teachers.comments.store', $teacher->id]
]) !!}
This is working. If I want to use the same CommentController to store the comments for a school, how should I modify the store method of the controller?
Adam's solution is great, but I would not hard-code the model's namespace that way. Instead, what I would do is make use of Laravel's Relation::morphMap(), you can check it out here: https://laravel.com/docs/5.6/eloquent-relationships#polymorphic-relations
That way, you will also make your database entries more readable. I recommend using a service provider to map the morphs.
Also, the Model base class has a getMorphClass() method, so instead of
$comment->commentable_type = 'App\\Models\\'.$model;
I would use
$comment->commentable_type = $model->getMorphClass();
That way you integrate Laravel's logic into your code.
Im not sure if this is the Laravel convension, but i have done the following:
Made a route:
Route::post('/Comment/{model}/{id}', [
// etc
]);
Then in the controller get the model and check against an array of allowed models, pass the id through and attach:
public function store(Request $request, $model, $id) {
$allowed = ['']; // list all models here
if(!in_array($model, $allowed) {
// return redirect back with error
}
$comment = new Comment();
$comment->user_id = $request->user()->id;
$comment->commentable_type = 'App\\Models\\'.$model;
$comment->commentable_id = $id;
$comment->body = $request->body;
$comment->save();
return redirect()->back();
}
Like I say, there is most likely a much better way to accomplish, but this is how I've done it. It keeps it short and sweet and checks if the model can take a comment.
I implemented this way if you want, according to me it's the one of the bests way to do that.
// Route::post('/comments/{model}/{id}', 'CommentController#store');
class CommentController extends Controller {
protected $model;
public function __construct()
{
$this->model = Relation::getMorphedModel(
request()->route()->parameter('model')
);
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
dd($this->model); // return 'App\Post' or null
}
}
For an API I'm currently building I'd like to be able to send a request with a JSON body with the following content
{"title": "foo"}
to create a new database record for an Entity called Project.
I made a controller which subclasses FOSRestController. To create a project, I made an action
/**
* #Route("/")
*
* #ApiDoc(
* section="Project",
* resource=true,
* input={"class"="AppBundle\Form\API\ProjectType"},
* description="Creates a new project",
* statusCodes={
* 201="Returned when successful",
* }
* )
*
* #Method("POST")
* #Rest\View(statusCode=201)
*/
public function createProjectAction(Request $request)
{
$project = new Project();
$form = $this->createForm(ProjectType::class, $project);
$form->submit(($request->request->get($form->getName())));
if ($form->isSubmitted() && $form->isValid()) {
return $project;
}
return View::create($form, 400);
}
The ProjectType looks like this
class ProjectType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Project'
));
}
}
However, when I try to post said JSON to the API, it responds that the title property cannot be blank, which is good because that's the validation rule set for it. However, it IS set. I suddenly realized I have to send the JSON prefixed by the actual object's name to make this work:
{"project":{"title": "bla"}}
Which feels a little strange to be fair, it should be enough to just post the properties.
So, based on this information I simply have 2 questions:
Why do I need to "submit" this form with ($request->request->get($form->getName())), shouldn't $request be enough?
What do I need to change for the FormType to validate the entity as is, instead of prefixing it with the entity's name?
Edit 1: adding or removing the data_class in the Default Options does not change the behaviour at all.
This is because of how Symfony Controller "createForm" helper method works. Reasoning behind it is that multiple forms could have same target URL. By prefixing with form name, Symfony can know which form was submitted.
This can be seen by looking at "createForm" method implementation:
public function createForm($type, $data = null, array $options = array())
{
return $this->container->get('form.factory')->create($type, $data, $options);
}
If you don't want this behavior, it's really easy to change it:
public function createProjectAction(Request $request)
{
$project = new Project();
$form = $this->get('form.factory')->createNamed(null, new ProjectType(), $project);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $project;
}
return View::create($form, 400);
}
So you're basically creating a "nameless" form. Since you're building an API, it's probably a good idea to pull this into a createNamelessForm($type, $data, $options) helper method in your base controller so that you don't have to get Form Factory from container explicitly all the time and make it easier on the eyes.
Comment on your edit
Wrapper key is not generated by "data_class" option, but by "getName()" method on your form type.
I would separate logic in my controllers.
In newWebsiteAction() I display my form. Next, I send data from form to postWebsiteAction() method.
If validation fails, I would like to redirect to senWebsiteAction() and display errors. What I should add to my code to do it? Because now I dont see errors
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\Website;
use AppBundle\Form\Type\WebsiteType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
class WebsitesController extends Controller
{
/**
* #Route("/websites", name="websites")
*/
public function getWebsiteAction()
{
return $this->render('websites/index.html.twig');
}
/**
* #Route("/websites/new", name="websites.new")
*/
public function newWebsiteAction()
{
$website = new Website();
$form = $this->createForm(new WebsiteType(), $website);
return $this->render('websites/create.html.twig', array(
'form' => $form->createView()
));
}
/**
* #Route("/websites/post", name="websites.post", methods={"POST"})
*/
public function postWebsiteAction(Request $request)
{
$form = $this->createFormBuilder()->getForm();
$form->handleRequest($request);
if($form->isValid())
{
$website = $form->getData();
$website->setUser($this->getUser());
$em =$this->getDoctrine()->getManager();
$em->persist($website);
$em->flush();
return $this->redirectToRoute('websites');
}
return $this->redirectToRoute('websites.new');
}
}
Hmm. IMHO you doing it wrong.
First: Why you wan't to do it in single action? If you want some separation better move some logic to services. Because store logic in services is Symfony2 way. E.g. Persist and flush logic to some EntityManager service (abstract example).
Second (hint): How you will get $form errors in newWebsiteAction()?
Edit:
Here is how I usually doing that:
public function createAction(Request $request)
{
$post = new Post();
$form = $this->createForm('post', $post);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return $this->redirect($this->generateUrl('show_post', ['slug_title' => $post->getSlugTitle()]));
}
return $this->render('GeekhubMainBundle:Post:create.html.twig', ['form' => $form->createView()]);
}
yes it looks like some logic will duplicated in different methods, but:
1) If method less than 20 lines all fine;
2) In your way you methods for handle form or create entity are not reusable;
Hope that will help :)
public function postWebsiteAction(Request $request)
{
$form = $this->createFormBuilder()->getForm();
$form->handleRequest($request);
if($form->isValid())
{
$website = $form->getData();
$website->setUser($this->getUser());
$em =$this->getDoctrine()->getManager();
$em->persist($website);
$em->flush();
return $this->redirectToRoute('websites');
} else {
$this->generateUrl('\your_defined_route');
return $this->redirect($url);
}
return $this->redirectToRoute('websites.new');
}
check out the else after $form is valid.
I made this Controller to play around with Symfony 2.3
namespace AskThem\MainBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use AskThem\MainBundle\Entity\Task;
class DefaultController extends Controller {
private $context = array();
public function indexAction(Request $request) {
$task = new Task();
$form =
$this->createFormBuilder($task)
->add("task", "text")
->add("dueDate", "date")
->add("save", "submit")
->add("saveAndAdd", "submit")
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
// ... perform some action, such as saving the task to the database
$nextAction = $form->get('saveAndAdd')->isClicked()
? 'homepage'
: 'task_success';
return $this->redirect($this->generateUrl($nextAction));
}
$this->context["form"] = $form->createView();
return $this->render('AskThemMainBundle:Default:index.html.twig', $this->context);
}
public function successAction(Request $request) {
return new Response("Victory");
}
}
The form was working fine, so then I added a validation-yml file with some NotBlank rules to test if it would work, and indeed it did.
But when I deleted those rules (I even deleted the whole file) the form still kept presenting itself with the required attribute. How do I turn off the validation?