i'm trying to queue an email sending invoice emails in laravel 5.1, i pass in a variable called invoice, when i dd($invoice->dateString()) in the Job class it's return the correct value but when i pass it in to the view the $invoice variable return empty array (so i get an error about trying to get property from non-object...).
the second problem i have is when i try to add attachment to the job it returns an error : "Serialization of closure failed: Serialization of 'SplFileInfo' is not allowed".
the job class looks like that:
namespace LM2\Jobs;
use Guzzle\Service\Client;
use LM2\Jobs\Job;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Contracts\Queue\ShouldQueue;
use LM2\Models\User as User;
use LM2\Models\Client as LMClient;
class SendInvoiceEmail extends Job implements SelfHandling, ShouldQueue
{
protected $user;
protected $invoice;
protected $attachment;
protected $update;
public function __construct(User $user, LMClient $client, $invoice,$update)
{
$this->user = $user;
$this->client = $client;
$this->invoice = $invoice;
$this->update = $update;
}
public function handle()
{
$attachment = $this->client->invoiceFile($this->invoice->id,['vendor' => 'Test','product' => 'Your Product']);
$invoice = $this->invoice;
$data = [
'invoice' => $this->invoice,
'update'=> $this->update,
];
$user = $this->user;
\Mail::queue('emails.invoices', $data , function($m) use ($user,$invoice,$attachment){
$m->to($user->email)->subject('New payment received')->attach($attachment);
});
}
}
and my controller function looks like that:
public function sendEmailInvoice($update = false){
$client = \Auth::client();
$user = \Auth::user();
$invoices = $client->invoices();
$this->dispatch(new SendInvoiceEmail($user,$client,$invoices[0],$update));
$activity = $data['update'] ? 'updated': 'added';
return ['success', $activity];
}
can someone please tell me what am i doing wrong?
thanks a lot you all :)
Just a guess... but when using Mail::queue() the $data get's converted/cast to an array/you lose your objects inside of the view - hence why you're receiving errors when trying to call methods(), because they don't exist.
Rather than passing invoice + update objects, get what you need from them in the handle method and construct the $data array.
$data = [
'invoice_foo' => $invoice->getFoo(),
'invoice_bar' => $invoice->getBar(),
];
*** Apologies if this doesn't help at all!
so i found the answer thanks to #Michael, i have changed my handle so it's look like this now:
public function handle(Mailer $mailer)
{
$client = $this->client;
$invoice = $this->invoice;
$data = [
'date' => $invoice->dateString(),
'amount' => $invoice->dollars(),
'update'=> $this->update,
];
$user = $this->user;
return $mailer->queue('emails.invoices', $data , function($m) use ($user,$client,$invoice){
$attachment = $client->invoiceFile($invoice->id,['vendor' => 'Infogamy','product' => 'Your Product']);
$m->to($user->email)->subject('New payment received')->attach($attachment);
});
}
The attachment should be processed inside the mailer callback function, and the function called from the $invoice variable (object) should be called inside the handle function and not in the blade view template.
Related
I am fairly new to ionic4/angular7. I am having trouble accessing an attribute in an observable.
show.service.ts
getDetails(id) {
return this.http.get(this.url+"get_shows.php?id="+id).pipe(map(results => results));
}
show-details.page.ts
ngOnInit() {
let id = this.activatedRoute.snapshot.paramMap.get('id');
this.show = this.showService.getDetails(id);
this.name = this.show.show_name; // e.g. Breaking Bad
}
Is it possible to access the data this way?
First, i'm belied you don't need this .pipe(map(results => results));
second http.Get returns an Observable see: https://angular.io/api/common/http/HttpClient .
You should to subscribe in order of get the attribute.
this.showService.getDetails(id).subscribe(value => {this.show = value;});
I'm new at phpspec (coming from phpunit) and I have problems setting the behavior of a mock returned by another mock.
I'm creating a wrapper class around the Guzzle client and I want to check the output of the response.
Here's the spec:
function it_gets_response_status_code(Client $client, Url $url, Response $response)
{
$this->beConstructedWith($client);
$url->__toString()->willReturn('http://example.com');
$data = ['foo' => 'bar'];
$response->getStatusCode()->willReturn(200);
$client->request('POST', $url, ['form_params' => $data])->willReturn($response);
$this->post($url, $data);
assert($this->getResponseStatusCode() === 200); // Failing! :(
}
and the corresponding functions in my class:
public function post(Url $url, array $data)
{
$this->response = $this->client->request('POST', (string) $url, ['form_params' => $data]);
}
public function getResponseStatusCode()
{
return $this->response->getStatusCode();
}
The assertion is failing and when I check what is this status code, I see that instead of the integer 200, it's an instance of PhpSpec\Wrapper\Subject. What am I missing here?
I've searched and googled but cannot find resources about using the mock returned by another mock in phpspec. I'm wondering if the reason for this is that it's a code smell? If so I'd be glad to see how I could do this differently (currently I cannot see how I could keep the code simple and doing differently).
try:
assert($this->getResponseStatusCode()->getWrappedObject() === 200);
this:
$response->getStatusCode()->willReturn(200)
returns a '200' string wrapped in an Subject object, on which you can then make mock/stub calls if needed. To get the real value of the subject you need to call getWrappedObject
in my ZF2 application I am adding the following event listener, however I want to make the execution of the action actually stop, however this doesnt happen.
public function setEventManager(EventManagerInterface $events)
{
parent::setEventManager($events);
$controller = $this;
$events->attach('dispatch', function ($e) use ($controller) {
$request = $e->getRequest();
$method = $request->getMethod();
$headers = $request->getHeaders();
// If we get here, based on some conditions, the original intended action must return a new JsonViewModel()...
return new JsonViewModel([]); // However, this doesn't do anything.
}, 100); // execute before executing action logic
}
Based on your comments, I am assuming you are doing some sort of authentication. You can perfecty use the event manager for this. However, I would not tie the listener to a single controller. If your API increases, you might want to split the API over several controllers and you get into trouble with your authentication.
My solution is to create a listener which listens to the dispatch event of the Zend\Mvc\Application. This is an event which is triggered before the event in the controllers itself.
use Zend\Mvc\MvcEvent;
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$em = $app->getEventManager();
$sm = $app->getServiceManager()->getSharedManager();
$listener = new Listener\Authentication();
$identifier = 'MyModule\Controller\ApiController';
$em->attach($identifier, MvcEvent::EVENT_DISPATCH, $listener, 1000);
}
This way, the listener is attached to all controllers which are identified with MyModule\Controller\ApiController. The listener will be triggered on every dispatch call of those controllers. Your listener can short-circuit the complete dispatch loop in case you need it:
use Zend\Http\Request as HttpRequest;
use Zend\Mvc\MvcEvent;
use Zend\Json\Json;
use Zend\View\Model\JsonModel;
class Authentication
{
public function __invoke(MvcEvent $e)
{
$request = $e->getRequest();
if (!$request instanceof HttpRequest) {
// Don't run on CLI requests
return;
}
if ($result->isValid()) {
// Say you get auth result and all is OK
return;
}
// Case 1: short-circuit and return response, this is the fast way
// The response I use here is the HTTP problem API
$message = array(
'httpStatus' => 401,
'title' => 'Unauthorized',
'detail' => 'You are unauthorized to perform this request',
);
$response = $e->getResponse();
$response->setStatusCode(401);
$response->getHeaders()->addHeaderLine('Content-Type', 'application/json');
$response->setContent(Json::encode($message);
return $response;
// Case 2: don't short circuit and stop propagation, you're using the JSON renderer
$e->getResponse()->setStatusCode(401);
$message = array(
'httpStatus' => 401,
'title' => 'Unauthorized',
'detail' => 'You are unauthorized to perform this request',
);
$model = new JsonModel($message);
return $model;
}
}
I would advice you to use the first method (returning the response yourself) because you'll short circuit the complete dispatch process and skip the complete finishing of the request. If you really rely on the view and response senders, use the second case.
Now if you need a controller which is authenticated via this system, add the identifier to this controller:
namespace MyModule\Controller;
use Zend\Mvc\Controller\AbstractActionController;
class MyFooBarApiController extends AbstractActionController
{
protected $eventIdentifer = 'MyModule\Controller\ApiController';
// your code here
}
If you need to allow certain requests without validation (I would always use a whitelist!), you can do this in your listener:
use Zend\Mvc\Route\RouteMatch;
$routematch = $e->getRouteMatch();
if (!$routematch instance of RouteMatch) {
// It's a 404, don't perform auth
return;
}
$route = $routematch->getMatchedRouteName();
if (
($request->isPost() && 'foo/bar' === $route)
|| ($request->isGet() && 'baz/bat' === $route)
) {
// We allow these requests to pass on without auth
return;
}
In your code you can explicitly check request method and route name. If you need parameters of the route, you can access it with $routematch->getParam('id').
Use the following in your event:
$e->stopPropagation();
So far, I have figured out how to return a typical JSON response in Zend Framework 2. First, I added the ViewJsonStrategy to the strategies section of the view_manager configuration. Then, instead of returning a ViewModel instance from the controller action, I return a JsonModel instance with all my variables set.
Now that I've figured that piece out, I need to understand how to render a view and return it within that JSON response. In ZF1, I was able to use $this->view->render($scriptName), which returned the HTML as a string. In ZF2, the Zend\View\View::render(...) method returns void.
So... how can I render an HTML view script and return it in a JSON response in one request?
This is what I have right now:
if ($this->getRequest()->isXmlHttpRequest()) {
$jsonModel = new JsonModel(...);
/* #todo Render HTML script into `$html` variable, and add to `JsonModel` */
return $jsonModel;
} else {
return new ViewModel(...);
}
OK, i think i finally understood what you're doing. I've found a solution that i think matches your criteria. Though i am sure that there is room for improvement, as there's some nasty handwork to be done...
public function indexAction()
{
if (!$this->getRequest()->isXmlHttpRequest()) {
return array();
}
$htmlViewPart = new ViewModel();
$htmlViewPart->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array(
'key' => 'value'
));
$htmlOutput = $this->getServiceLocator()
->get('viewrenderer')
->render($htmlViewPart);
$jsonModel = new JsonModel();
$jsonModel->setVariables(array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1,2,3,4,5,6)
));
return $jsonModel;
}
As you can see, the templateMap i create is ... nasty ... it's annoying and i'm sure it can be improved by quite a bit. It's a working solution but just not a clean one. Maybe somehow one would be able to grab the, probably already instantiated, default PhpRenderer from the ServiceLocator with it's template- and path-mapping and then it should be cleaner.
Thanks to the comment ot #DrBeza the work needed to be done could be reduced by a fair amount. Now, as I'd initially wanted, we will grab the viewrenderer with all the template mapping intact and simply render the ViewModel directly. The only important factor is that you need to specify the fully qualified template to render (e.g.: "$module/$controller/$action")
I hope this will get you started though ;)
PS: Response looks like this:
Object:
html: "<h1>Hello World</h1>"
jsonArray: Array[6]
jsonVar1: "jsonVal2"
You can use more easy way to render view for your JSON response.
public function indexAction() {
$partial = $this->getServiceLocator()->get('viewhelpermanager')->get('partial');
$data = array(
'html' => $partial('MyModule/MyPartView.phtml', array("key" => "value")),
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6));
$isAjax = $this->getRequest()->isXmlHttpRequest());
return isAjax?new JsonModel($data):new ViewModel($data);
}
Please note before use JsonModel class you need to config View Manager in module.config.php file of your module.
'view_manager' => array(
.................
'strategies' => array(
'ViewJsonStrategy',
),
.................
),
it is work for me and hope it help you.
In ZF 3 you can achieve the same result with this code
MyControllerFactory.php
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
$renderer = $container->get('ViewRenderer');
return new MyController(
$renderer
);
}
MyController.php
private $renderer;
public function __construct($renderer) {
$this->renderer = $renderer;
}
public function indexAction() {
$htmlViewPart = new ViewModel();
$htmlViewPart
->setTerminal(true)
->setTemplate('module/controller/action')
->setVariables(array('key' => 'value'));
$htmlOutput = $this->renderer->render($htmlViewPart);
$json = \Zend\Json\Json::encode(
array(
'html' => $htmlOutput,
'jsonVar1' => 'jsonVal2',
'jsonArray' => array(1, 2, 3, 4, 5, 6)
)
);
$response = $this->getResponse();
$response->setContent($json);
$response->getHeaders()->addHeaders(array(
'Content-Type' => 'application/json',
));
return $this->response;
}
As usual framework developer mess thing about AJAX following the rule why simple if might be complex Here is simple solution
in controller script
public function checkloginAction()
{
// some hosts need to this some not
//header ("Content-type: application/json"); // this work
// prepare json aray ....
$arr = $array("some" => .....);
echo json_encode($arr); // this works
exit;
}
This works in ZF1 and ZF2 as well
No need of view scrpt at all
If you use advise of ZF2 creator
use Zend\View\Model\JsonModel;
....
$result = new JsonModel($arr);
return $result;
AJAX got null as response at least in zf 2.0.0
I've created simple ViewHelper with http://blog.evan.pro/creating-a-simple-view-helper-in-zend-framework-2. How to get URL params in this helper? $this->params('param') works only in controllers...
Given the code from the blog post, you can use this code from inside the view helper:
$this->request->getPost('param'); // post parameter
// or
$this->request->getQuery('param'); // query parameter
The code from the example receives an instance of the Zend\Http\Request object for the current request and stores it in the property called request of the view helper so you can use the request property to access the Request object and information from it.
In view helper you have to add code like this:
Module.php
'factories' => array(
'myViewHelper' => function($pm) {
return new MyView($pm);
},
)
Now in Helper Class file your have to add following piece of code
public function __construct($pm) {
$this->pluginManager = $pm;
$this->serviceLocator = $this->pluginManager->getServiceLocator();
$this->routeMatch = $this->serviceLocator->get('Router')->match($this->serviceLocator->get('Request'));
}
public function __invoke() {
$params = $this->getRouteMatch()->getParams();
}
Here $params will return all route params in array formate.