I'm still learning on how to setup Swiftmailer as a service, I believe I have a working solution but need some help on how to call this in the controller.
How do I call this service in my controller? (service code, original code before service and service.yml below)
Edit:
I am trying to call it like so:
$emailManager = $this->container->get('email_manager');
$content = $emailManager->sendMail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText);
But am getting a undefined variable error:
Notice: Undefined variable: subject in /.../DefaultController.php line 58
EmailManager service
namespace Acme\EmailBundle\Service;
use Symfony\Component\HttpFoundation\RequestStack;
class EmailManager
{
private $request;
private $mailer;
public function __construct(RequestStack $requestStack, \Swift_Mailer $mailer)
{
$this->request = $requestStack->getCurrentRequest();
$this->mailer = $mailer;
}
public function sendMail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText)
{
/* #var $mailer \Swift_Mailer */
if(!$this->mailer->getTransport()->isStarted()){
$this->mailer->getTransport()->start();
}
/* #var $message \Swift_Message */
$message = $this->mailer->createMessage();
$message->setSubject($subject);
$message->setBody($bodyHtml, 'text/html');
$message->addPart($bodyText, 'text/plain', 'UTF8');
$message->addTo($recipientEmail, $recipientName);
$message->setFrom( array('example#gmail.com' => 'Chance') );
$this->mailer->send($message);
$this->mailer->getTransport()->stop();
}
}
Original controller code for sending emails prior to putting it in as a service
/**
* #Route("/", name="contact")
* #Template("AcmeEmailBundle:Default:index.html.twig")
*/
public function contactAction(Request $request)
{
$form = $this->createForm(new ContactType());
if ($request->isMethod('POST')) {
$form->submit($request);
if ($form->isValid()) {
$message = \Swift_Message::newInstance()
->setSubject($form->get('subject')->getData())
->setFrom($form->get('email')->getData())
->setTo('example#gmail.com')
->setBody(
$this->renderView(
'AcmeEmailBundle:Default:index.html.twig',
array(
'ip' => $request->getClientIp(),
'name' => $form->get('name')->getData(),
'message' => $form->get('message')->getData()
)
)
);
$this->get('mailer')->send($message);
$request->getSession()->getFlashBag()->add('success', 'Your email has been sent! Thanks!');
return $this->redirect($this->generateUrl('contact'));
}
}
return array(
'form' => $form->createView()
);
}
services.yml
services:
email_manager:
class: Acme\EmailBundle\Service\EmailManager
arguments: [#request_stack, #mailer]
scope: request
When your controller extends Controller like so
<?php
namespace Acme\DemoBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
//...
/**
* DemoController
*/
class DemoController extends Controller
{
// ...
}
You can access the services like this:
$emailManager = $this->container->get('email_manager');
You can then send your email like this:
$emailManager->sendEmail($subject, $recipientName, $recipientEmail, $bodyHtml, $bodyText);
Complete Overview
1 Create an Email Manager that will compose your emails and send them
<?php
namespace Acme\EmailBundle\Manager;
//...
/**
* Composes and Sends emails
*/
class EmailManager
{
/**
* The mailer
*
* #var \Swift_Mailer
*/
protected $mailer;
/**
* The email address the mailer will send the emails from
*
* #var String
*/
protected $emailFrom;
/**
* #param Request $mailer;
*/
public function __construct(\Swift_Mailer $mailer, $emailFrom)
{
$this->mailer = $mailer;
$this->emailFrom = $emailFrom;
}
/**
* Compose email
*
* #param String $subject
* #param String $recipientEmail
* #param String $bodyHtml
* #return \Swift_Message
*/
public function composeEmail($subject, $recipientEmail, $bodyHtml)
{
/* #var $message \Swift_Message */
$message = $this->mailer->createMessage();
$message->setSubject($subject)
->setBody($bodyHtml, 'text/html')
->setTo($recipientEmail)
->setFrom($this->emailFrom);
return $message;
}
/**
* Send email
*
* #param \Swift_Message $message;
*/
public function sendEmail(\Swift_Message $message)
{
if(!$this->mailer->getTransport()->isStarted()){
$this->mailer->getTransport()->start();
}
$this->mailer->send($message);
$this->mailer->getTransport()->stop();
}
}
3 Declare it as a service
parameters:
acme_email.email_from: example#gmail.com
services:
email_manager:
class: Acme\EmailBundle\Service\EmailManager
arguments: [#mailer,%acme_email.email_from%]
]
4 Create a Form Handler that will handle your contact forms
<?php
namespace Acme\ContactBundle\Form\Handler;
use Symfony\Component\Form\Form;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Acme\EmailBundle\Manager\EmailManager;
/**
* Handles Contact forms
*/
class ContactFormHandler
{
/**
* The request
*
* #var Symfony\Component\HttpFoundation\Request;
*/
protected $request;
/**
* The Template Engine
*/
protected $templating;
/**
* The email manager
*/
protected $emailManager;
/**
* #param Request $request;
* #param TwigEngine $templating
* #param EmailManager $emailManager
*/
public function __construct(Request $request, TwigEngine $templating, EmailManager $emailManager)
{
$this->request = $request;
$this->templating =$templating;
$this->emailManager = $emailManager;
}
/**
* Processes the form with the request
*
* #param Form $form
* #return Email|false
*/
public function process(Form $form)
{
if ('POST' !== $this->request->getMethod()) {
return false;
}
$form->bind($this->request);
if ($form->isValid()) {
return $this->processValidForm($form);
}
return false;
}
/**
* Processes the valid form, sends the email
*
* #param Form
* #return EmailInterface The email sent
*/
public function processValidForm(Form $form)
{
/** #var EmailInterface */
$email = $this->composeEmail($form);
/** Send Email */
$this->emailManager->sendEmail($email);
return $email;
}
/**
* Composes the email from the form
*
* #param Form $form
* #return \Swift_Message
*/
public function composeEmail(Form $form)
{
$subject = $form->get('subject')->getData();
$recipientEmail = $form->get('email')->getData();
$bodyHTML = $this->templating->renderView(
'AcmeEmailBundle:Default:index.html.twig',
array(
'ip' => $this->request->getClientIp(),
'name' => $form->get('name')->getData(),
'message' => $form->get('message')->getData()
)
);
/** #var \Swift_Message */
return $this->emailManager->composeEmail($subject, $recipientEmail, $bodyHTML);
}
}
3 Declare it as a service:
services:
acme_contact.contact_form_handler:
class: Acme\ContactBundle\FormHandler\ContactFormHandler
arguments: [#request, #templating, #email_manager]
scope: request
4 Which gives you sthg short and sweet in your controller
/**
* #Route("/", name="contact")
* #Template("AcmeContactBundle:Default:index.html.twig")
*/
public function contactAction(Request $request)
{
/** BTW, you could create a service to create the form too... */
$form = $this->createForm(new ContactType());
$formHandler = $this->container->get('acme_contact.contact_form_handler');
if ($email = $formHandler->process($form)) {
$this->setFlash('success', 'Your email has been sent! Thanks!');
return $this->redirect($this->generateUrl('contact'));
}
}
Related
I want to attach invoice pdf instead of packing slip while creating shipment.
I am using Fooman Email attachment extension version 2.0.8
My magento is 2.2.5 Can anyone know how can I change the attched PDF in shipping confirmation mail ?
Currently it is attaching packing slip but I want to attach invoice pdf in shipping confirmation mail.
We had a similar problem using fooman. We also wanted to send our invoice on shipment creation, while disableing the standard transactional invoice mail on invoice creation. I wrote a module that sends the invoice email together with the shipping email, which is not exactly what you are looking for but maybe you can leverage that.
The module is pretty simple. Except from boilerplate registration.php and module.xml, all you need is to override InvoiceOrder from Magento\Sales\Model and comment out this line:
$this->notifierInterface->notify($order, $invoice, $comment);
like so:
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Magento\Sales\Model;
use Magento\Framework\App\ResourceConnection;
use Magento\Sales\Api\Data\InvoiceCommentCreationInterface;
use Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface;
use Magento\Sales\Api\InvoiceOrderInterface;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order\Config as OrderConfig;
use Magento\Sales\Model\Order\Invoice\NotifierInterface;
use Magento\Sales\Model\Order\InvoiceDocumentFactory;
use Magento\Sales\Model\Order\InvoiceRepository;
use Magento\Sales\Model\Order\OrderStateResolverInterface;
use Magento\Sales\Model\Order\PaymentAdapterInterface;
use Magento\Sales\Model\Order\Validation\InvoiceOrderInterface as InvoiceOrderValidator;
use Psr\Log\LoggerInterface;
/**
* Class InvoiceOrder
* #SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class InvoiceOrder implements InvoiceOrderInterface
{
/**
* #var ResourceConnection
*/
private $resourceConnection;
/**
* #var OrderRepositoryInterface
*/
private $orderRepository;
/**
* #var InvoiceDocumentFactory
*/
private $invoiceDocumentFactory;
/**
* #var PaymentAdapterInterface
*/
private $paymentAdapter;
/**
* #var OrderStateResolverInterface
*/
private $orderStateResolver;
/**
* #var OrderConfig
*/
private $config;
/**
* #var InvoiceRepository
*/
private $invoiceRepository;
/**
* #var InvoiceOrderValidator
*/
private $invoiceOrderValidator;
/**
* #var NotifierInterface
*/
private $notifierInterface;
/**
* #var LoggerInterface
*/
private $logger;
/**
* InvoiceOrder constructor.
* #param ResourceConnection $resourceConnection
* #param OrderRepositoryInterface $orderRepository
* #param InvoiceDocumentFactory $invoiceDocumentFactory
* #param PaymentAdapterInterface $paymentAdapter
* #param OrderStateResolverInterface $orderStateResolver
* #param OrderConfig $config
* #param InvoiceRepository $invoiceRepository
* #param InvoiceOrderValidator $invoiceOrderValidator
* #param NotifierInterface $notifierInterface
* #param LoggerInterface $logger
* #SuppressWarnings(PHPMD.ExcessiveParameterList)
*/
public function __construct(
ResourceConnection $resourceConnection,
OrderRepositoryInterface $orderRepository,
InvoiceDocumentFactory $invoiceDocumentFactory,
PaymentAdapterInterface $paymentAdapter,
OrderStateResolverInterface $orderStateResolver,
OrderConfig $config,
InvoiceRepository $invoiceRepository,
InvoiceOrderValidator $invoiceOrderValidator,
NotifierInterface $notifierInterface,
LoggerInterface $logger
) {
$this->resourceConnection = $resourceConnection;
$this->orderRepository = $orderRepository;
$this->invoiceDocumentFactory = $invoiceDocumentFactory;
$this->paymentAdapter = $paymentAdapter;
$this->orderStateResolver = $orderStateResolver;
$this->config = $config;
$this->invoiceRepository = $invoiceRepository;
$this->invoiceOrderValidator = $invoiceOrderValidator;
$this->notifierInterface = $notifierInterface;
$this->logger = $logger;
}
/**
* #param int $orderId
* #param bool $capture
* #param array $items
* #param bool $notify
* #param bool $appendComment
* #param \Magento\Sales\Api\Data\InvoiceCommentCreationInterface|null $comment
* #param \Magento\Sales\Api\Data\InvoiceCreationArgumentsInterface|null $arguments
* #return int
* #throws \Magento\Sales\Api\Exception\DocumentValidationExceptionInterface
* #throws \Magento\Sales\Api\Exception\CouldNotInvoiceExceptionInterface
* #throws \Magento\Framework\Exception\InputException
* #throws \Magento\Framework\Exception\NoSuchEntityException
* #throws \DomainException
*/
public function execute(
$orderId,
$capture = false,
array $items = [],
$notify = false,
$appendComment = false,
InvoiceCommentCreationInterface $comment = null,
InvoiceCreationArgumentsInterface $arguments = null
) {
$connection = $this->resourceConnection->getConnection('sales');
$order = $this->orderRepository->get($orderId);
$invoice = $this->invoiceDocumentFactory->create(
$order,
$items,
$comment,
($appendComment && $notify),
$arguments
);
$errorMessages = $this->invoiceOrderValidator->validate(
$order,
$invoice,
$capture,
$items,
$notify,
$appendComment,
$comment,
$arguments
);
if ($errorMessages->hasMessages()) {
throw new \Magento\Sales\Exception\DocumentValidationException(
__("Invoice Document Validation Error(s):\n" . implode("\n", $errorMessages->getMessages()))
);
}
$connection->beginTransaction();
try {
$order = $this->paymentAdapter->pay($order, $invoice, $capture);
$order->setState(
$this->orderStateResolver->getStateForOrder($order, [OrderStateResolverInterface::IN_PROGRESS])
);
$order->setStatus($this->config->getStateDefaultStatus($order->getState()));
$invoice->setState(\Magento\Sales\Model\Order\Invoice::STATE_PAID);
$this->invoiceRepository->save($invoice);
$this->orderRepository->save($order);
$connection->commit();
} catch (\Exception $e) {
$this->logger->critical($e);
$connection->rollBack();
throw new \Magento\Sales\Exception\CouldNotInvoiceException(
__('Could not save an invoice, see error log for details')
);
}
if ($notify) {
if (!$appendComment) {
$comment = null;
}
//$this->notifierInterface->notify($order, $invoice, $comment);
}
return $invoice->getEntityId();
}
}
Now all you have to do is setup events.xml and observe sales_order_shipment_save_after
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name='sales_order_shipment_save_after'>
<observer name='SendInvoiceWithShipment' instance='Vendor\Module\Observer\SendInvoiceWithShipment'
/>
</event>
</config>
Observer leverages standard Magento 2 transactional email $this->_invoiceSender->send($invoice); like so:
<?php
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\Email\Sender\InvoiceSender;
class SendInvoiceWithShipment implements ObserverInterface
{
protected $_invoiceSender;
public function __construct(
InvoiceSender $invoiceSender
) {
$this->_invoiceSender = $invoiceSender;
}
public function execute(\Magento\Framework\Event\Observer $observer)
{
$order = $observer->getShipment()->getOrder();
if (!$order) {
// Dont send invoice if order is not provided
return;
}
$invoices = $order->getInvoiceCollection();
foreach ($invoices as $invoice) {
try {
$this->_invoiceSender->send($invoice);
} catch (\Exception $e) {
// Do something if failed to send
}
}
}
}
I have some problems for testing a creation form (for a simple Bank entity) in a SF3 project. Here the test code:
class BankControllerTest extends WebTestCase
{
/**
* Test the creation form action
*/
function testNewAction()
{
$client = static::createClient();
// User must be logged in
$client->request('GET', '/bank/bank/new');
$response = $client->getResponse();
$this->assertEquals(302, $response->getStatusCode());
// Page request test
$client = LoginControllerTest::createClientConnectedTest();
$crawler = $client->request('GET', '/bank/bank/new');
$response = $client->getResponse();
$this->assertEquals(200, $response->getStatusCode());
// Form testing
$formData = array(
'bank[name]' => 'BankTest '.uniqid(),
'bank[comment]' => 'Tast bank '.uniqid().' comment.',
);
$submitButtonCrawlerNode = $crawler->selectButton('Create');
$form = $submitButtonCrawlerNode->form();
$client->submit($form, $formData);
$response = $client->getResponse();
$container = $client->getContainer();
$this->assertEquals(301, $response->getStatusCode());
}
}
The part which interesting me is the last "Form testing" part, I pasted everything in case of I made a mistake before.
Instead of the "301" httpCode I want, I have a 500 error :
vendor/bin/phpunit src/BillBrother/BankAccountBundle
[...]
1) BillBrother\BankAccountBundle\Test\Controller\BankControllerTest::testNewAction
Failed asserting that 500 matches expected 301.
/home/developer/src/BillBrother/BankAccountBundle/Test/Controller/BankControllerTest.php:47
And saw it is an "AlreadySubmittedException" which generate this error :
[2016-06-30 08:00:18] request.CRITICAL: Uncaught PHP Exception Symfony\Component\Form\Exception\AlreadySubmittedException: "You cannot add children to a submitted form" at /home/developer/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php line 802 {"exception":"[object] (Symfony\\Component\\Form\\Exception\\AlreadySubmittedException(code: 0): You cannot add children to a submitted form at /home/developer/vendor/symfony/symfony/src/Symfony/Component/Form/Form.php:802)"} []
Did I do something in a wrong way?
Thanks in advance for your help.
Here some other parts of code I used in this test:
The Login test file, from where I use the createClientConnectedTest() static function :
/**
* BillBrother\AuthBundle\Controller test class file.
*
* #TODO Write tests
* #author Neimheadh <contact#neimheadh.fr>
*/
namespace BillBrother\AuthBundle\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\BrowserKit\Cookie;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
/**
* BillBrother\AuthBundle\Controller test class.
*/
class LoginControllerTest extends WebTestCase
{
/**
* Create a connected client.
*
* #param string $login
* #param string $password
* #return \Symfony\Bundle\FrameworkBundle\Client
*/
public static function createClientConnected($login, $password)
{
return static::createClient(array(), array(
'PHP_AUTH_USER' => $login,
'PHP_AUTH_PW' => $password
));
}
/**
* Create a client connected with test account
*
* #return \Symfony\Bundle\FrameworkBundle\Client
*/
public static function createClientConnectedTest()
{
return static::createClientConnected(
'test#email.com',
'password'
);
}
/**
* Test the login form
*/
public function testLoginform()
{
$client = static::createClient();
$crawler = $client->request('GET', '/login');
}
}
The BankType class :
/**
* Bank entity type
*
* #author Neimheadh <contact#neimheadh.fr>
*/
namespace BillBrother\BankAccountBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
/**
* Bank type form
*/
class BankType extends AbstractType
{
/**
* Build the form
*
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('comment', TextareaType::class)
;
}
/**
* Configure the form default options.
*
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'BillBrother\BankAccountBundle\Entity\Bank'
));
}
}
The Bank entity class :
/**
* BillBrother bank entity class file.
*
* #author Neimheadh <contact#neimheadh.fr>
*/
namespace BillBrother\BankAccountBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use BillBrother\AuthBundle\Entity\User;
use Datetime;
/**
* Bank
*
* #ORM\Table(name="bank")
* #ORM\Entity(repositoryClass="BillBrother\BankAccountBundle\Repository\BankRepository")
*/
class Bank
{
/**
* The bank id.
*
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Bank creator
*
* The user account who created the bank.
*
* If null, it means the bank was created by the system.
*
* #var User
*
* #ORM\JoinColumn(name="creator_id", nullable=true)
* #ORM\ManyToOne(targetEntity="BillBrother\AuthBundle\Entity\User")
*/
private $creator;
/**
* The bank name.
*
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* Comments about the bank.
*
* #var string
*
* #ORM\Column(name="comment", type="text")
*/
private $comment;
/**
* Bank row creation date
*
* #var Datetime
*
* #ORM\Column(name="create_date", type="datetime")
*/
private $createDate;
/**
* Constuctor
*
* Initialize the creation date to the current date
*/
public function __construct()
{
$this->createDate = new Datetime;
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set name, nullable=tru
* #return Bank
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set comment
*
* #param string $comment
*
* #return Bank
*/
public function setComment($comment)
{
$this->comment = $comment;
return $this;
}
/**
* Get comment
*
* #return string
*/
public function getComment()
{
return $this->comment;
}
/**
* Set creator
*
* #param User $creator
*
* #return Bank
*/
public function setCreator(User $creator = null)
{
$this->creator = $creator;
return $this;
}
/**
* Get creator
*
* #return User
*/
public function getCreator()
{
return $this->creator;
}
}
The controller called :
/**
* BillBrother bank account bundle bank controller class file.
*
* #author Neimheadh <contact#neimheadh.fr>
*/
namespace BillBrother\BankAccountBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use BillBrother\BankAccountBundle\Entity\Bank;
use BillBrother\BankAccountBundle\Form\Type\BankType;
/**
* BillBrother application bank account bank controller.
*/
class BankController extends Controller
{
/**
*/
private function _buildForm(Request $request, Bank $bank)
{
$form = $this->createForm(BankType::class, $bank);
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($bank);
$em->flush();
}
$form->add('submit', SubmitType::class, array(
'label' => 'Create'
));
return $form;
}
/**
* Bank account bundle bank list page action.
*
* #Route("/banks", name="billbrother_bankaccount_bank_list")
*/
public function listAction()
{
return $this->render('BillBrotherBankAccountBundle:Bank:list.html.twig');
}
/**
* Create a new bank page action.
*
* #param Request $request
*
* #Route("/bank/new", name="billbrother_bankaccount_bank_new")
*/
public function newAction(Request $request)
{
$bank = new Bank();
$form = $this->_buildForm($request, $bank);
if($form->isValid())
return $this->redirectToRoute(
'billbrother_bankaccount_bank_edit',
array('id'=>$bank->getId())
);
return $this->render(
'BillBrotherBankAccountBundle:Bank:form.html.twig',
array(
'form' => $form->createView()
)
);
}
/**
* Modify a bank page action.
*
* #Route("/bank/edit/{id}", name="billbrother_bankaccount_bank_edit")
*/
public function editAction($id)
{
return $this->render('BillBrotherBankAccountBundle:Bank:form.html.twig');
}
}
And about versions :
✗ bin/console --version
Symfony version 3.1.1 - app/dev/debug
✗ vendor/bin/phpunit --version
PHPUnit 5.4.6 by Sebastian Bergmann and contributors.
✗ php --version
PHP 7.0.6 (cli) (built: May 24 2016 03:29:56) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies
I saw where my problem was from. In my Controller :
private function _buildForm(Request $request, Bank $bank)
{
$form = $this->createForm(BankType::class, $bank);
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($bank);
$em->flush();
}
$form->add('submit', SubmitType::class, array(
'label' => 'Create'
));
return $form;
}
We see I add my "submit" button after the form handleRequest(). By moving the code :
$form->add('submit', SubmitType::class, array(
'label' => 'Create'
));
just after my form creation, everything is ok.
The final function code :
private function _buildForm(Request $request, Bank $bank)
{
$form = $this->createForm(BankType::class, $bank);
$form->add('submit', SubmitType::class, array(
'label' => 'Create'
));
$form->handleRequest($request);
if($form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($bank);
$em->flush();
}
return $form;
}
I'm using Fosuser and Fosrest for a webservice.
I created GetUser to get List user and the registration to register an user and it work perfectly !
But, now I try to make the update user but i got a problem !
I copy the function from Fosuser controller
In My controller :
public function updatePostAction(Request $request,$id)
{
$user = $this->getUser();
if (!is_object($user) || !$user instanceof UserInterface) {
throw new AccessDeniedException('This user does not have access to this section.');
}
/** #var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
$dispatcher = $this->get('event_dispatcher');
$event = new GetResponseUserEvent($user, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_INITIALIZE, $event);
if (null !== $event->getResponse()) {
return $event->getResponse();
}
/** #var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
$formFactory = $this->get('fos_user.profile.form.factory');
$form = $formFactory->createForm();
$form->setData($user);
$form->handleRequest($request);
if ($form->isValid()) {
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
$event = new FormEvent($form, $request);
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_SUCCESS, $event);
$userManager->updateUser($user);
if (null === $response = $event->getResponse()) {
$url = $this->generateUrl('fos_user_profile_show');
$response = new RedirectResponse($url);
}
$dispatcher->dispatch(FOSUserEvents::PROFILE_EDIT_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
return $response;
}
return $this->render('FOSUserBundle:Profile:edit.html.twig', array(
'form' => $form->createView()
));
}
it doesn't work ! anyone can help me ?
When i try to post :
{
"fos_user_profile_form": {
"username":"jacky"
}
}
it returns : https://gist.github.com/gaticho/34ae4fff5492fdecd48f
The database doesn't change :-(
My User entity :
<?php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
private $dateCreated;
/**
* #var DateTime
*
* #ORM\Column(name="date_modified", type="datetime", nullable=true)
*/
private $dateModified;
/**
* #var DateTime
*
* #ORM\Column(name="date_deleted", type="datetime", nullable=true)
*/
private $dateDeleted;
public function __construct()
{
parent::__construct();
}
}`
Thanks !
I'm writing a REST API client and I am trying to unit test the user creation process which let a user to upload an image.
I am using Symfony 2.7, Doctrine Extension Bundle (Uploadable extension) and FOS Rest Bundle
The unit tests are working well excepted when I try to upload a file, it triggers me the following error when I error_log the 500 HTTP Reponse :
The class 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' was not found in the chain configured namespaces
Please find the relevant code :
UsersControllerTest.php
<?php
namespace Acme\Bundle\UserBundle\Tests\Controller;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class UsersControllerTest extends WebTestCase
{
public function testAvatar(){
$client = static::createClient();
$shortImage = tempnam(sys_get_temp_dir(), 'upl');
imagepng(imagecreatetruecolor(10, 10), $shortImage);
$file = new UploadedFile(
$shortImage,
basename($shortImage),
MimeTypeGuesser::getInstance()->guess($shortImage),
filesize($shortImage)
);
$crawler = $client->request(
"POST",
"/api/users",
array(
"user_registration" => array(
"firstName" => "test",
"lastName" => "test"
),
),
array(
'user_registration'=>array('avatar'=>$file)
),
array(
'Content-Type' => 'multipart/formdata'
)
);
error_log($client->getResponse()); //Here I see the Uploadable class namespace error
$this->assertEquals(200, $client->getResponse()->getStatusCode());
}
}
UserRegistrationType.php
<?php
namespace Acme\Bundle\UserBundle\Form\Type;
use Acme\Bundle\UserBundle\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormBuilderInterface;
class UserRegistrationType extends AbstractType{
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\Bundle\UserBundle\Entity\User',
'cascade_validation' => true,
'csrf_protection' => false
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('firstName', 'text');
$builder->add('lastName', 'text');
$builder->add('avatar', 'file', array(
'required' => false
));
}
public function getParent()
{
return 'form';
}
public function getName()
{
return 'user_registration';
}
}
UsersController.php
<?php
namespace Acme\Bundle\UserBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations\View;
use Acme\Bundle\UserBundle\Entity\User;
use Acme\Bundle\UserBundle\Entity\Avatar;
use Acme\Bundle\UserBundle\Form\Type\UserRegistrationType;
use Symfony\Component\HttpFoundation\Request;
use FOS\RestBundle\Controller\Annotations\RouteResource;
use JMS\SecurityExtraBundle\Annotation as JMSSecurity;
class UsersController extends FOSRestController
{
/**
* #View(serializerGroups={"Registration"})
*/
public function postUsersAction(Request $request){
$user = new User();
$form = $this->createForm(new UserRegistrationType(), $user);
$form->submit($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($user);
if(null !== $user->getAvatar()){
$uploadableManager = $this->get('stof_doctrine_extensions.uploadable.manager');
$uploadableManager->markEntityToUpload($user, $user->getAvatar());
}
$em->flush();
return $user;
}
else {
$validator = $this->get('validator');
$errors = $validator->validate($user, array('Default','Registration'));
$view = $this->view($errors, 400);
return $this->handleView($view);
}
}
}
Avatar.php
<?php
namespace Acme\Bundle\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Avatar
*
* #ORM\Table("avatar")
* #ORM\Entity
* #Gedmo\Uploadable(pathMethod="getPath", callback="postUploadAction", filenameGenerator="SHA1", allowOverwrite=true, appendNumber=true)
*/
class Avatar
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="path", type="string")
* #Gedmo\UploadableFilePath
*/
private $path;
/**
* #ORM\Column(name="name", type="string")
* #Gedmo\UploadableFileName
*/
private $name;
/**
* #ORM\Column(name="mime_type", type="string")
* #Gedmo\UploadableFileMimeType
*/
private $mimeType;
/**
* #ORM\Column(name="size", type="decimal")
* #Gedmo\UploadableFileSize
*/
private $size;
public function postUploadAction(array $info)
{
// Do some stuff with the file..
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getPath(){
return __DIR__.'../../web/avatars/';
}
/**
* Set path
*
* #param string $path
* #return Image
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Set mimeType
*
* #param string $mimeType
* #return Image
*/
public function setMimeType($mimeType)
{
$this->mimeType = $mimeType;
return $this;
}
/**
* Get mimeType
*
* #return string
*/
public function getMimeType()
{
return $this->mimeType;
}
/**
* Set size
*
* #param string $size
* #return Image
*/
public function setSize($size)
{
$this->size = $size;
return $this;
}
/**
* Get size
*
* #return string
*/
public function getSize()
{
return $this->size;
}
}
User.php
class User{
...
/**
* Avatar
*
* #ORM\OneToOne(targetEntity="Acme\Bundle\UserBundle\Entity\Avatar", cascade={"all"})
* #ORM\JoinColumn(nullable=true)
* #Assert\Valid
*/
private $avatar;
public function getAvatar(){
return $this->avatar;
}
public function setAvatar($avatar = null){
$this->avatar = $avatar;
return $this;
}
}
Error stack (JSON)
For full stack please see here : http://pastebin.com/sgPE4Uh1
{
"error":{
"code":500,
"message":"Internal Server Error",
"exception":[
{
"message":"The class 'Symfony\\Component\\HttpFoundation\\File\\UploadedFile' was not found in the chain configured namespaces Acme\\Bundle\\UserBundle\\Entity",
"class":"Doctrine\\Common\\Persistence\\Mapping\\MappingException",
"trace":[
{
"namespace":"",
"short_class":"",
"class":"",
"type":"",
"function":"",
"file":"D:\\xampp\\htdocs\\RestApi\\vendor\\doctrine\\common\\lib\\Doctrine\\Common\\Persistence\\Mapping\\MappingException.php",
"line":37,
"args":[
]
}]
}
}
}
I really don't know the origin of the error, On my config.yml the doctrine mapping is set to auto
Thank you in advance for any help
EDIT 1
I have completely refactored the code with this scenario :
The upload request is now independant, it has it own isolated request with a Content-Type set to multipart/form-data
This time it would not have any problem, but..the same error is still here :
{
code: 500
message: "The class 'Symfony\Component\HttpFoundation\File\UploadedFile' was not found in the chain configured namespaces Sowq\Bundle\UserBundle\Entity"
}
I am wondering if that's not a bug or a compatibility issue between Symfony 2.7 and the doctrine Uploadable extension, any idea?
I think the problem is that you or (uploadableManager extension - I don't know this) try set an object file (of type "UploadedFile Class")
$Avatar is a file object if you want save to DataBase, you need a datatype blob.
Usually, before save the file on File System and then save $Avatar on DB, where $Avatar is just file path or file name o link to File System resource
see https://github.com/Atlantic18/DoctrineExtensions/issues/1353
I'm have a problem embedding forms from different entities in one form, my form is being displayed with firstname [input] lastname [input] address - but the address has no input next to it.
Basically I want to create a form where the user can add first name, last name, address1, address2, city, country ect and submit it it as one, although it's different tables.
The main form is no problem the only issue I'm having is with the second embedded form. Any help would be greatly appreciated.
Here is my code:
Member class:
namespace Pomc\MembersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Pomc\MembersBundle\Entity\Member
*/
class Member
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $firstName
*/
private $firstName;
/**
* #var string $lastName
*/
private $lastName;
/**
* #var Pomc\MembersBundle\Entity\Address
*/
private $address;
/**
* #var Pomc\MembersBundle\Entity\Telephone
*/
private $telephone;
public function __construct()
{
$this->address = new \Doctrine\Common\Collections\ArrayCollection();
$this->telephone = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName
*
* #param string $firstName
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
}
/**
* Get firstName
*
* #return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set lastName
*
* #param string $lastName
*/
public function setLastName($lastName)
{
$this->lastName = $lastName;
}
/**
* Get lastName
*
* #return string
*/
public function getLastName()
{
return $this->lastName;
}
/**
* Add address
*
* #param Pomc\MembersBundle\Entity\Address $address
*/
public function addAddress(\Pomc\MembersBundle\Entity\Address $address)
{
$this->address[] = $address;
}
/**
* Get address
*
* #return Doctrine\Common\Collections\Collection
*/
public function getAddress()
{
return $this->address;
}
/**
* Add telephone
*
* #param Pomc\MembersBundle\Entity\Telephone $telephone
*/
public function addTelephone(\Pomc\MembersBundle\Entity\Telephone $telephone)
{
$this->telephone[] = $telephone;
}
/**
* Get telephone
*
* #return Doctrine\Common\Collections\Collection
*/
public function getTelephone()
{
return $this->telephone;
}
}
Here is the address class:
namespace Pomc\MembersBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Pomc\MembersBundle\Entity\Address
*/
class Address
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $addressType
*/
private $addressType;
/**
* #var string $firstLine
*/
private $firstLine;
/**
* #var string $secondLine
*/
private $secondLine;
/**
* #var string $city
*/
private $city;
/**
* #var string $postCode
*/
private $postCode;
/**
* #var string $country
*/
private $country;
/**
* #var Pomc\MembersBundle\Entity\Member
*/
private $member;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set addressType
*
* #param string $addressType
*/
public function setAddressType($addressType)
{
$this->addressType = $addressType;
}
/**
* Get addressType
*
* #return string
*/
public function getAddressType()
{
return $this->addressType;
}
/**
* Set firstLine
*
* #param string $firstLine
*/
public function setFirstLine($firstLine)
{
$this->firstLine = $firstLine;
}
/**
* Get firstLine
*
* #return string
*/
public function getFirstLine()
{
return $this->firstLine;
}
/**
* Set secondLine
*
* #param string $secondLine
*/
public function setSecondLine($secondLine)
{
$this->secondLine = $secondLine;
}
/**
* Get secondLine
*
* #return string
*/
public function getSecondLine()
{
return $this->secondLine;
}
/**
* Set city
*
* #param string $city
*/
public function setCity($city)
{
$this->city = $city;
}
/**
* Get city
*
* #return string
*/
public function getCity()
{
return $this->city;
}
/**
* Set postCode
*
* #param string $postCode
*/
public function setPostCode($postCode)
{
$this->postCode = $postCode;
}
/**
* Get postCode
*
* #return string
*/
public function getPostCode()
{
return $this->postCode;
}
/**
* Set country
*
* #param string $country
*/
public function setCountry($country)
{
$this->country = $country;
}
/**
* Get country
*
* #return string
*/
public function getCountry()
{
return $this->country;
}
/**
* Set member
*
* #param Pomc\MembersBundle\Entity\Member $member
*/
public function setMember(\Pomc\MembersBundle\Entity\Member $member)
{
$this->member = $member;
}
/**
* Get member
*
* #return Pomc\MembersBundle\Entity\Member
*/
public function getMember()
{
return $this->member;
}
}
Here is the memberform:
namespace Pomc\MembersBundle\Form\Type;
use \Symfony\Component\Form\AbstractType;
use \Symfony\Component\Form\FormBuilder;
class MemberType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('firstName');
$builder->add('lastName');
$builder->add('address','collection', array( 'type' => new AddressType(),
'allow_add' => true,
'prototype' => true,
'by_reference' => false,
));
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Pomc\MembersBundle\Entity\Member');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
function getName()
{
return 'member';
}
}
Here is the address form:
namespace Pomc\MembersBundle\Form\Type;
use \Symfony\Component\Form\AbstractType;
use \Symfony\Component\Form\FormBuilder;
class AddressType extends AbstractType
{
public function buildForm(Formbuilder $builder, array $options)
{
$builder->add('firstLine');
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Pomc\MembersBundle\Entity\Address');
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
function getName()
{
return 'address';
}
function getIdentifier()
{
return 'address';
}
}
Here is the controller:
namespace Pomc\MembersBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use \Pomc\MembersBundle\Entity\Member;
use \Symfony\Component\HttpFoundation\Request;
use \Pomc\MembersBundle\Form\Type\MemberType;
class DefaultController extends Controller
{
public function indexAction($name)
{
return $this->render('PomcMembersBundle:Default:index.html.twig', array('name' => $name));
}
public function newAction(Request $request)
{
$member = new Member();
$form = $this->get('form.factory')->create(new MemberType());
if($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if($form->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($member);
$em->flush();
}
}
return $this->render('PomcMembersBundle:Default:new.html.twig', array( 'form'=> $form->createView(),));
}
}
Here is the template:
<form action="{{ path('member_new') }}" method="post" {{ form_enctype(form)}}>
{{ form_widget(form) }}
<div>
{{ form_row(form.address)}}
</div>
<input type="submit" />
</form>
Been a long time user of this site, but this is my first question.
Thank you
Oh I faced the same problem, but I found the solution, hope this will help you :-)
You're forgetting to add an Address object to the member entity.
In your action you'll need to do the following:
$member = new Member();
$member->addAddress(new Address());
$form = $this->createForm(new MemberType(), $member);
And then in your template:
{% for address in form.address %}
{{ form_widget(address.firstLine) }}
{% endfor %}
Btw your 'firstline' widget doesn't relate to an entity property.
Btw if you called addAddress two times, you would of course get two 'firstline' widgets in your form.
Hope this works. best of luck.
Llewellyn, do you mean something like thid:
public function editAction($id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('imBundle:Inspecciones')->find($id);
$entity_valores = $em->getRepository('imBundle:ValoresInspecciones')->findByInspecciones($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Inspecciones entity.');
}
$entity->setValoresInspecciones($entity_valores);
$editForm = $this->createEditForm($entity);
$deleteForm = $this->createDeleteForm($id);
return $this->render('imBundle:Inspecciones:edit.html.twig', array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}