Laravel - Mailer Service? - php

I'm a bit of a noob with Laravel but I'm setting up a bug tracking system and have a question about how to remove multiple instances of sending mails. Let me show you how I've got it set up currently:
public function store(UsersRequest $request)
{
$user = User::create($request->all());
Mail::queue('emails.master', ['user' => $user], function($message) use ($user) {
$message->to('someone#somewhere.com')
->subject('New User Created');
});
return redirect('/users');
}
So I have this method in a controller which creates a new user in my system, then I send the mail out. What I'd like to do is strip the mail call out to one line of code.
What's the best way to achieve this?
What I've found so far, is setting up a service to handle this - like this: http://lukefair.com/create-a-mailer-service-with-laravel-and-a-basic-working-example-of-dependency-injection/
This seems like a good idea to my noob brain and would achieve what I need it to. Is there a better way to do this than creating a service? Making use of events perhaps (although, I guess I'd still need a service for that)?
As I said, I'm a bit of a Laravel noob and haven't got these patterns and ways of doing things down yet.
Any advice would be great. Thanks!

The typical solution for problems like this is, in the Laravel world at least, to create a service provider. The article you linked is a good fit for what you are looking to do.
If you want to be able to access your new mailer class globally as you can with Mail and Request and any of the other familiar Laravel facades, you can create your own and it will be usable from anywhere in your application.

Related

Laravel testing. Is possible to use multiple calls in same test?

I have this test:
public function test_user_can_access_the_application_page(
{
$user=[
'email'=>'user#user.com',
'password'=>'user1234',
];
$response=$this->call('POST','/login',$user);
$this->assertAuthenticated();
$response->assertStatus(302)
->assertRedirect('/dashboard')
->assertLocation('/dashboard');
$response=$this->call('GET','/application/index');
$response->assertLocation('/application/index');
}
After I log in, it directs me to the dashboard ok until now, but if I want to access the other page after that, I cant. This error comes up.
Expected :'http://mock.test/application/index'
Actual :'http://mock.test'
Aren't multiple calls allowed in the same test, or is another way to access other pages after login?
(Note: It's not possible to use factories for the actingAs so I need to login).
If you can't use factories for actingAs, then you should try with cookie.
Look at the https://github.com/firebase/php-jwt library.
I guess you will need to call the function as an user, since you can only access it logged in. Laravel provides the actingAs() method for such cases.
https://laravel.com/docs/7.x/http-tests#session-and-authentication
You can create a random User who has the permission to log into your app or take a seeded one and call the function acting as the chosen User.
$response=$this->actingAs($user)->call('GET','/application/index');
If you call it without actingAs(), your middleware will redirect you back to the login or home screen (what you defined in the LoginController ).
In my opinion this test case should have its own testing method. I recommend using a test method per route or per use case. It makes your tests clearly arranged and easy to understand.
If you want to be authenticated, the easiest way is to have PHPUnit simulate authentication using the actingAs() method.
This method makes the user authenticated, so you wouldn't want to test the login method with it. You should write your login tests separate from testing the other pages.
To answer your question, yes you can make multiple requests in the same test, but in this case linking the login test to the 'application/index' page likely does not make much sense.
public function test_the_user_can_login()
{
$user = [
'email'=>'user#user.com',
'password'=>'user1234',
];
$response = $this->call('POST','/login',$user);
$this->assertAuthenticated();
$response->assertStatus(302)
->assertRedirect('/dashboard')
->assertLocation('/dashboard');
}
public function test_user_can_access_the_application_page()
{
$user = User::where($email, "user#user.com")->first();
$response = $this->actingAs($user)
->call('GET','/application/index');
$response->assertLocation('/application/index');
}
I experienced that in laravel 8 I use comment #test and involved second test!!! I mean if you use two function for test you must us #test that php artisan test, test both of them.

Symfony 4 : Logout a users in a custom controller

I have followed the tutorial to make may application able to logout users simply by calling a route like /logout (Via the Security module as described in the official documentation). It works.
Now I would like to logout the user (still logged via the described in the doc "Remember me" function) in my own controllers (For example before an email validation, in case another session is still opened under another account).
But none of my methods works, it makes me crazy. I have tried $session->clear(), $session->invalidate(), $request->getSession->clear(), $request->getSession->Invalidate(), etc. etc. Nothing works.
So my question are, please: How do you do it? How should I handle this case? Is it related to the "remember me" functionality (maybe it's managed in another cookie or something?) ?
Thanks in advance
Your guess might be right, that the issue could be related to the remember me functionality as this will use cookies to store the token, instead of the session, and therefore need a different LogoutHandler.
Symfony provides multiple ways to handle authentication and you will need the correct LogoutHandler(s) depending on your current settings.
Solving your issue is surprisingly hard if you don't just want to redirect the user to the logout path. The "best" way I can think of right now, is simulating a logout-request by building the Request-object manually and then dispatching a GetResponseEvent with it so, that the LogoutListener will be triggered. Dispatching the event might have weird side effects, so you might even want to trigger the listener directly. It could look something like this:
use Symfony\Component\HttpKernel\HttpKernelInterface;
use Symfony\Component\Security\Http\Firewall\ListenerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
class MyController
{
private $kernel;
private $logoutListener;
public function __construct(HttpKernelInterface $kernel, LogoutListenerInterface $logoutListener)
{
$this->kernel = $kernel;
$this->logoutListener = $logoutListener;
}
private function customLogout()
{
// This needs to be updated whenever the logout path in your security.yaml changes. Probably something you want to improve on.
$request = Request::create('/logout');
$event = new GetResponseEvent($this->kernel, $request);
$this->logoutListener->handle($event);
}
public function someAction()
{
$this->customLogout();
// Do whatever you want to do for your request
}
}
I don't think this is a good solution as interfering with the security system is inherently dangerous. Directly calling the LogoutListener and passing around the kernel are also a bit iffy. To be honest I'm not even 100% sure this will work, but this is the closest to what I could come up with as a possible solution to your problem. You might want to rethink what you are doing and find an alternative approach.

Model Binding in Symfony

From few days I'm struggling with implementation of model binding mechanism known from ASP MVC into world of Symfony REST, but with no luck.
I encountered something similar - #ParamConverter, but from tries I made it looks like its not what I'm looking for.
My attempts ended at Body Converter. When I POSTed my form-data to desired route : /api/posts I received error about not existing route. debug:router gave me route different that i expected : /api/posts/{post}.{_format}. It seems to me that Body converters want me to send data in json or xml, but i want to send form-data because of files. Solution what im looking for should work like this :
/**
* #ParamConverter("Post", class="BlogBundle:Post")
*/
public function postPostAction(Post $post)
{
if($post->isValid())
{
$em->persist($post);
}
}
Do anyone can give some clues, or implementation how to make it work? Because i belive I'm not only one who want to achive such elegant way

ZF2 Cron Job call to Model

I am looking for help on ZF2 cron jobs. I will try to explain this the best I can. I am Fairly new with ZF2, sorry.
As a basic example, I have a cron job that runs through a list of active users. Currently the only way that works is recreating calls from my user model class, instead of reusing code already in place.
Example of work works
$this->usersTableGateway = new \Zend\Db\TableGateway\TableGateway('users', $dbAdapterTemp);
$users = $this->usersTableGateway->select(array('active' => 1));
This provides my users.
Why am I unable to use
if (!$this->usersTable) {
$sm = $this->getServiceLocator();
$this->usersTable = $sm->get('UserManagement\Model\UsersTable');
}
$this -> usersTable() -> getActiveUsers();
Is there a workaround for this for me to be able to call functions within my users model class? I am probably missing something really basic. I thought it had something to do with getServiceLocator but this statement works.
$this->getServiceLocator()->get('Config');
Thanks for the help in advance. Please let me know if you need more information.

How to solve some problems in symfony 2?

I started to explore the world of Symfony 2 now and face with some realy strange problems i would not think they can occure in such a professional framework. I will show you the problems i face one by one:
1) How to get the recent actionName?
I found only this solution which is imho semiprofessional:
$request->attributes->get('_controller');
// will get yourBundle\Controller\yourController::CreateAction
$params = explode('::',$request->attributes->get('_controller'));
// $params[1] = 'createAction';
$actionName = substr($params[1],0,-6);
Is this serious, i have to do some extra-work to get it, why.. Is there a better solution? Creating a base controller class with a method e.g. getActionName(), but why do i have to implement such basic functionality in a framework. Is there a other way?
2) When i forward a request the code in 1) will not work.
$request = $this->container->get('request');
$getParameterList = $request->query->all();
if (!empty($getParameterList['mode'])
&& $getParameterList['mode'] == 1) {
return $this->forward('AcmeDemoBundle:Routing:lawyersearch', array(), $getParameterList);
}
The reason why it will not work is that "AcmeDemoBundle:Routing:lawyersearch" is a other format than when i came directly from a route. Second problem here is that i have to forward the GET-paramters as well(i think POST too). Is there a way that i do not have to care about it?
3) How to use a default template without using this annotation:
/**
* #Template()
*/
public function indexAction()
{
return array();
}
I do not want to have above all my methods this annotation; i know i can put it on the top of the class definition. Is there a way to achieve this? The only solution i see, is to write a BaseController that determines by a method out of the module/controller/action the default template.
4) I found classes that use public attributes e.g. Symfony\Component\Validator\Constraints\Length with e.g. public $max;
How to solve this? Very strange because this is not professional to use public attributes.
I hope someone has easy solutions for this. It would be realy dissapointing if Symfony 2 has so much strange behaviour in so much cases. 4 strange things i 2 days since i began to explore it. It gives me the feeling that there is much more when i continue.
Please confirm that there are no other solution by the framework or which is the solution. Thank you
1) By accessing the '_controller' parameter of the request, you are delving into the internals of Symfony2. They rarely document anything related to this outside of routing. You should use controller actions more definitively, don't try to automate too much on this level.
2) Symfony2 can't account for highly dynamic controllers. You know it is possible to call ->forward more than once, and within the same controller action. This creates a nesting nightmare that the Symfony developers weren't prepared to deal with.
This is one of the reasons $request = $this->container->get('request'); is now deprecated in favour of $stack = $this->container->get('request_stack');. Because forwarding needs to create new internal requests.
3) Also deprecated. Symfony2 best practices now discourages the use of #Template() with empty parameters because of the potentially volatile development of actions/templates. You are supposed to explicitly define which template to use, if you use one at all. This comes in handy when dealing with data-only responses. You wouldn't want your responses to use a template automatically as this would result in unexpected behaviour in your design.
1) Use Constant: __FUNCTION__
http://php.net/manual/en/language.constants.predefined.php
2) Try setMethod on $request:
$this->get('request')->setMethod('POST');
3) I do not know, probably not possible.
4) Symfony\Component\Validator\Constraints\Length is one of constraints:
http://symfony.com/doc/current/book/validation.html#constraints

Categories