I am writing tests for a small Symfony 6 project. The project contains a custom class representing a web form request. The class has methods for sending various emails using the Symfony MailerInterface. For example:
public function sendConfirmationEmail(): array
{
if (!$this->validated) {
return [
'success' => false,
'message' => 'Inquiry has not been validated. Use Inquiry->validate() first',
];
}
if (!$this->email) {
return [
'success' => false,
'message' => 'Benutzer hat keine Emailadresse angegeben'
];
}
$email = (new TemplatedEmail())
->to($this->email)
->subject('Eingangsbestätigung')
->htmlTemplate('confirmationEmail.html.twig')
->textTemplate('confirmationEmail.txt.twig')
->context([
'name' => $this->name,
'mail' => $this->email,
'phone' => $this->phone,
'subject' => $this->subject,
'message' => $this->message,
]);
try {
$this->mailer->send($email);
$this->logger->debug('Confirmation mail sent');
return [
'success' => true,
'message' => 'Email wurde gesendet',
];
} catch (TransportExceptionInterface $e) {
$this->logger->debug('Error sending confirmation email: ' . $e);
return [
'success' => false,
'message' => 'Email konnte nicht gesendet werden: ' . $e,
];
}
}
The mailer is passed to the constructor of the class as a read only variable:
public function __construct(
private readonly LoggerInterface $logger,
private readonly MailerInterface $mailer,
private readonly array $officeRecipients,
private readonly ValidatorInterface $validator,
) {
}
I have no trouble testing the successful cases using the Symfony KernelTestCase class, but I also want to test the catch block of the method in a unit test.
How can I simulate the TransportException during testing to trigger the catch block?
Related
I'm working on a project where a user is able to upload a file. My code works when a single file is uploaded, but I need to change it so a user is able to upload multiple files.
I want to store the files in my database as String. Currently it is stored as example: "file1.png". When uploading multiple files I would like it to be stored as "file1.png;file2.png;file3.png".
However when I add the "multiple => true" in the form, I get an error when pressing submit by the validator that the input needs to be a String.
My best guess is that I need to use Data transformers, but after reading the docs I still don't know how to approach this. ?
Data Transform
This is the controller (currently it expects a single file, as for multiple I would use foreach):
\#\[Route('/new', name: 'app_blog_new', methods: \['GET', 'POST'\])\]
\#\[IsGranted('IS_AUTHENTICATED')\]
public function new(Request $request, BlogRepository $blogRepository, SluggerInterface $slugger, MailerInterface $mailer): Response
{
$blog = new Blog();
$form = $this-\>createForm(BlogType::class, $blog);
$form-\>handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$additionalImages = $form->get('additional_images')->getData();
if ($additionalImages) {
$originalFilename = pathinfo($additionalImages->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = $slugger->slug($originalFilename);
$newFilename = $safeFilename . '-' . uniqid() . '.' . $additionalImages->guessExtension();
try {
$additionalImages->move(
$this->getParameter('blogimage_directory'),
$newFilename
);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
$blog->setAdditionalImages($newFilename);
}
}
If I add "multiple => true' to this form I get an "expected String" error on the front.
This is the form used to upload images to a blog:
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('title')
->add('additional_images', FileType::class, [
'label' => 'Additional images',
'mapped' => false,
'multiple' => true,
'required' => false,
'constraints' => [`your text`
new File([
'maxSize' => '1024k',
'mimeTypes' => [
'image/*',
],
'mimeTypesMessage' => 'Please upload a valid image',
])
],
]);
$builder->get('additional_images')
->addModelTransformer(new CallbackTransformer(
function ($additionalAsArray) {
// transform the array to a string
return implode('; ', $additionalAsArray);
},
function ($additionalAsString) {
// transform the string back to an array
return explode('; ', $additionalAsString);
}
))
;
}
This is the blog entity class which contains the image(s)
#[ORM\Entity(repositoryClass: BlogRepository::class)]
class Blog
{
#[ORM\Column(type: Types::TEXT, nullable: true)]
private ?string $additional_images = null;
}
I tried adding 'multiple => true' to the form and it works, as the user is able to select multiple files. But after submitting I get "implode(): Argument #1 ($pieces) must be of type array, string given"
I found out that all I had to do was add "new All" to the form:
->add('additional_images', FileType::class, [
'label' => 'Additional images',
'mapped' => false,
'required' => false,
'multiple' => true,
'constraints' => [
new All([
new File([
'maxSize' => '1024k',
'mimeTypes' => [
'image/*',
],
'mimeTypesMessage' => 'Please upload a valid image',
])
])
],
]);
And made my controller work with an array:
$additionalImages = $form->get('additional_images')->getData();
if ($additionalImages) {
$result = array();
foreach ($additionalImages as $image)
{
$originalFilename = pathinfo($image->getClientOriginalName(), PATHINFO_FILENAME);
$safeFilename = $slugger->slug($originalFilename);
$newFilename = $safeFilename . '-' . uniqid() . '.' . $image->guessExtension();
try {
$image->move(
$this->getParameter('blogimage_directory'),
$newFilename
);
} catch (FileException $e) {
// ... handle exception if something happens during file upload
}
$result[] = $newFilename;
}
$blog->setAdditionalImages(implode(";", $result));
}
I know that segments are not supported in the current v1Beta api (https://analyticsdata.googleapis.com/v1beta/properties/{property_id}:runReport).
Is there a workaround to get segmented data from using this api?
Reference: https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport
I was working with google analytics API GA4 using PHP hope this helps you
<?php
class analytics{
private $property_id =null;
private $client = null;
public $propertys;
public function setPropertyId($propertyid)
{
return $this->property_id = $propertyid;
}
private function getCredentials()
{
return DIR.'PATH/credentials.json');
}
private function getClient(): BetaAnalyticsDataClient
{
return $this->client= new BetaAnalyticsDataClient([
'credentials' => $this->getCredentials(),
]);
}
public function get($propertyid,$startdate,$enddate,dimensions,$metrics)
{
$response = $this->getClient()->runReport([
'property' => 'properties/' . $this->setPropertyId($propertyid),
'dateRanges' => [
new DateRange([
'start_date' => $startdate,
'end_date' => $enddate,
]),
],
'dimensions' => [new Dimension(
[
'name' => $dimensions,
]
),
],
'metrics' => [new Metric(
[
'name' => $metrics,
]
)
]
]);
foreach ($response->getRows() as $row) {
return $row->getMetricValues()[0]->getValue();
}
}
}
then you can use the get function with dates range and dimensions metrics you want
I build a FormType in Symfony5 and make use of DataTransformer on 2 fields :
compagnie_princ
compagnie_sec.
DataTransformer basically takes an object ID and renders It to a label.
DataTransformer works fine, when Form is initially rendered on browser, see below capture:
Problem is after validation callbacks are executed, If an error occured, It fails to transform my Id back to a text value.
Code samples (most important parts) :
AccordCommercialFormType.php
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('compagnie_princ', TextType::class,
[
'label' => 'forms.parameter.accord.compagnie_princ',
]
)
->add('compagnie_sec', TextType::class,
[
'label' => 'forms.parameter.accord.compagnie_sec',
]
);
/** ... **/
$builder>addEventListener(
FormEvents::PRE_SET_DATA,
[$this, 'onPreSetData']
)
->addEventListener(
FormEvents::PRE_SUBMIT,
[$this, 'onPreSubmit']
);
$builder->get('compagnie_princ')->addModelTransformer($this->transformer);
$builder->get('compagnie_sec')->addModelTransformer($this->transformer);
}
Events are captured on preSubmit to fetch ID, because fields 'compagnie_princ' and 'compagnie_sec' are autocompleted with AJAX, populating hidden inputs. My guess is something is going wrong on that part.
public function onPreSetData(FormEvent $event): void
{
$form = $event->getForm();
$form->add('compagnie_princ_id', HiddenType::class,
['mapped' => false,
'attr' => ['class' => 'hidden-field'],
'data' => $event->getData()->getCompagniePrinc() ? $event->getData()->getCompagniePrinc()->getId() : null
]
);
$form->add('compagnie_sec_id', HiddenType::class,
['mapped' => false,
'attr' => ['class' => 'hidden-field'],
'data' => $event->getData()->getCompagnieSec() ? $event->getData()->getCompagnieSec()->getId() : null,
]
);
}
public function onPreSubmit(FormEvent $event): void
{
$data = $event->getData();
$data['compagnie_princ'] = (int)$data['compagnie_princ_id'];
$data['compagnie_sec'] = (int)$data['compagnie_sec_id'];
$event->setData($data);
}
CompagnieToIdTransformer.php
class CompagnieToIdTransformer implements DataTransformerInterface
{
public function __construct(private EntityManagerInterface $em){
}
public function transform($compagnie)
{
if (null === $compagnie) {
return '';
}
return $compagnie->getCodeIata();
}
public function reverseTransform($compagnieId):?Compagnie
{
if (!$compagnieId) {
return null;
}
$compagnie = $this->em
->getRepository(Compagnie::class)
->find($compagnieId)
;
if (null === $compagnie) {
throw new TransformationFailedException(sprintf(
'A company with number "%s" does not exist!',
$compagnieId
));
}
return $compagnie;
}
}
ERROR:
I am trying to login as admin and i am defined the guards but there is an error in the validator that validator() must be of the type array in 158 line.
BranchController:
public function authenticateBranchAdmin(Request $request){
$validator = Validator($request, [
Line 158-> 'email' => 'required|email',
'password' => 'required'
]);
if($validator->passes()){
if(Auth::guard('branch')->attempt([
'email' => $request->email,
'password' => $request->password,
])){
return redirect('/branch'.'/'.Auth::guard('branch')->id);
}else{
if($this->AdminIsVerified($request->email)){
$request->session()->flash('message', 'Invalid email or password!');
}else{
$request->session()->flash('message', 'Please Register this Account!');
}
return redirect('/Admin/login');
}
}else{
return redirect('/Admin/login')->withErrors($validator)->withInput();
}
}
You need to convert $request to array, as array is expected by Validator, so instead of $request use $request->all().
I am new to cakephp and started learning cakephp3.2 directly
I have created a function in my custom controller as follows.
public function login(){
if($this->request->is('post')){
$user = $this->auth->identify();
if($user){
$this->Auth->setUser($user);
return $this->redirect($this->Auth->redirectUrl());
}else{
$this->Flash->error(__('Username or password is incorrect'), [
'key' => 'auth'
]);
}
}
}
class AppController extends Controller
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth',[
'authenticate' => [
'Form' => [
'fields' => [
'username' => 'email',
'password' => 'password'
]
]
],
'authError' => 'Did you really think you are allowed to see that?',
'loginAction' => [
'controller' => 'Users',
'action' => 'login'
],
'storage'=>'Session'
]);
$this->Auth->allow(['display']);
}
public function beforeRender(Event $event)
{
if (!array_key_exists('_serialize', $this->viewVars) &&
in_array($this->response->type(), ['application/json', 'application/xml'])
) {
$this->set('_serialize', true);
}
}
}
I don't understand how to deal with it.Should I load any external component or helper ?