the difference when using the initialization function in php - laravel

I'm not understanding how to use the initialization function in the code below. Can you explain it to me?
Class A
{
protected $classB;
public function __construct()
{
$this->classB = new ClassB();
}
}
Class A
{
protected $classB;
public function __construct(ClassB $classB)
{
$this->classB = $classB;
}
}

in the second way you always have to make your object like this.
$clasa = new ClassA($classb);
in the first way you are creating the object inside of the constructor.
the second way is an injection. and could be usefull when for example you need some attributes already seted in the object.

Related

Laravel mock multiple dependency

I have a Controller that has a dependency with BillingService, and BillingService has another dependency on UserService.
I need to call the Controller method getPlans and in this call I need to mock two functions:
loadPlans that is inside BillingService
getUsage that is in UserService
This is the full example:
class BillingPlanController
{
public function __construct(private BillingPlanService $billingPlanService)
{
}
public function getPlans()
{
$plans = $this->billingPlanService->getPlans();
//
}
}
class BillingPlanService
{
public function __construct(private UserService $userService)
{
}
public function getPlans()
{
$plans = $this->loadPlans();
$user = auth()->user();
$usage = $this->userService->getUsage(user); // DO SOMETHING, NEED TO MOCK .. HOW ?
}
public function loadPlans()
{
// DO SOMETHING, NEED TO MOCK .. HOW ?
}
}
At the end, in my test i simply call:
getJson(action([BillingPlanController::class, "getPlans"]));
In other tests, I'm able to mock a single Service, but in this scenario, I don't know how to write the mocks.
Sorry if I don't provide any "tries", but I really don't know how I can do that.
UPDATE
I tried to use partialMock and mock, but I get this error (when getUsage is called) - partialMock is used because i just need to mock a single function:
Typed property App\Modules\Billing\Services\BillingPlanService::$userService must not be accessed before initialization
$this->mock(UserService::class, function ($mock) {
$mock->shouldReceive("getUsage")->andReturn([]);
});
$this->partialMock(BillingPlanService::class, function ($mock) {
$mock->shouldReceive("loadPlans")->andReturn([]);
});
getJson(action([BillingPlanController::class, "getPlans"]));
Your exception in your partial mock, is because when you mock the BillingPlanService you do not intilize the userService due to it being a mock. You can simply set it on the mock and i think it should work in your context.
$userServiceMock = $this->mock(UserService::class, function ($mock) {
$mock->shouldReceive("getUsage")->andReturn([]);
});
$this->partialMock(BillingPlanService::class, function ($userServiceMock) use ($userServiceMock) {
$mock->set('userService', $userServiceMock);
$mock->shouldReceive("loadPlans")->andReturn([]);
});

How to set listener queue name from environment variable?

I just noticed that some of my listeners do not use the queue I expected them to use. Our team upgraded from Laravel 5.2 to 5.5 a few weeks back, and I guess this is when the problem started happening. There hasn't been much load on the system, so I only discovered it by accident.
Anyway. I used to set the queue name on the listener through a queue method, like so:
public function queue(QueueManager $handler, $method, $arguments): void
{
$handler->connection()->push($method, $arguments, Queue::getNotificationQueue());
}
This approach is not working anymore, so a default queue ends up handling the job instead of the expected notification queue.
So I looked at the documentation https://laravel.com/docs/5.5/events#queued-event-listeners, which states that the name should be set on a queue property on the listener. My problem is that I have the queue name in an environment variable, so I cannot just set it directly as a property, as shown in the documentation and it does not work to set it on the constructor, like so:
protected $queue;
public function __construct()
{
$this->queue = Queue::getNotificationQueue();
}
Does anyone here have an idea of how I can get around this?
Specifically for SQS queues the $queue property acts a bit weird because it doesn't seem to refer to queues defined in queue.php, but it expects a full queue url, so even the example in the documentation seems off.
But for dynamic queue names on queued event listeners that for example changes depending on environment, making a custom SqsConnector and SqsQueue will be one way to solve your issue.
Here is an example of implementation.
ACMEEventListener.php
class ACMEEventListener implements ShouldQueue
{
public function handle(Event $event): void
{
// I'm going to a custom queue
}
public static function getQueue(): string
{
return 'https://sqs.eu-central-1.amazonaws.com/<account id>/<queue name>';
}
}
CustomSqsConnector.php
use Illuminate\Queue\Connectors\SqsConnector;
use Aws\Sqs\SqsClient;
class CustomSqsConnector extends SqsConnector
{
public function connect(array $config)
{
$sqs = new SqsClient($config);
return new CustomSqsQueue($sqs, $config['queue']);
}
}
CustomSqsQueue.php
class CustomSqsQueue extends \Illuminate\Queue\SqsQueue
{
public function push($job, $data = '', $queue = null)
{
if ($job instanceof CallQueuedListener && method_exists($job->class, 'getQueue')) {
$queue = $job->class::getQueue();
}
return $this->pushRaw($this->createPayload($job, $data), $queue);
}
}
CustomSqsQueueServiceProvider.php
class CustomSqsQueueServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->booted(function () {
$this->app['queue']->extend('custom_sqs', function () {
return new CustomSqsConnector;
});
});
}
}
And then in your queue.php, your default SQS connection driver from sqs to custom_sqs

Laravel model properties

I'm not sure how to best ask the question. I'm trying to write DRY code and I've got a Job model. A job can have types (ex: full-time, part-time, etc). If I want to only define the types once where would I put them that I could use them everywhere? Is the best way to do this something like:
class Job extends Model
{
private static $jobTypes = [
'full-time',
'part-time',
];
public static function jobTypes()
{
return self::$jobTypes;
}
}
Or does doing a completely separate class make more sense / increase flexibility?
class JobTypes
{
private static $jobTypes = [
'full-time',
'part-time',
];
public static function jobTypes()
{
return self::$jobTypes;
}
}
Or something else...
Based on the requirement, your first response would be the best practice:
class Job extends Model
{
private static $jobTypes = [
'full-time',
'part-time',
];
public static function jobTypes()
{
return self::$jobTypes;
}
}
However, sometimes it's beneficial to have all of these variables defined at one place so that you can modify them easily. So if you have more static options like this for various models or for whatever reason, I would suggest to go with config file and define them in: config->app.php
and then you can access them from anywhere like so:
config('app.jobTypes')
Makes sense?

How can I filter fields before they reach Input::get("my_field")?

I want for example to remove the word "fck" of all input fields. I know I can do it after but it would be great if I can do it before so it applies to my entire app.
In other words, where do I have to modify the Input (or http or request) class of Laravel 4?
Well, there are a few possibilities. The easiest and simplest one is to simply make a new Input facade and override its get method. Something like this:
app/extensions/FilterableInput.php
use Illuminate\Support\Facades\Input as IlluminateInput;
class FilterableInput extends IlluminateInput {
public static function get($key = null, $default = null)
{
return static::filterInput(parent::get($key, $default));
}
// Filtering method
protected static function filterInput($value)
{
if (is_string($value))
{
return str_replace('fck', '***', $value);
}
return $value;
}
}
Don't forget to replace the Input alias on app/config/app.php with FilterableInput and to add app/extensions to your composer.json autoload.classmap settings.

Zend Framework: How to stop dispatch/controller execution?

I have a Zend Framework controller with an editAction().
class WidgetController extends BaseController
{
public function editAction()
{
//code here
}
}
This controller extends a base controller which checks if the user is logged in before allowing the user to edit a record.
class BaseController extends Zend_Controller_Action
{
public function init()
{
if ($this->userNotLoggedIn()) {
return $this->_redirect('/auth/login');
}
}
}
However, now that I am performing an AJAX request, I will be sending a JSON response back, so a redirect will no longer work. I need to stop further controller execution so I can immediately send a response:
class BaseController extends Zend_Controller_Action
{
public function init()
{
if ($this->userNotLoggedIn()) {
if ($this->_request->isXmlHttpRequest()) {
$jsonData = Zend_Json::encode(array('error'=>'You are not logged in!'));
$this->getResponse()
->setHttpResponseCode(401)
->setBody($jsonData)
->setHeader('Content-Type', 'text/json');
//now stop controller execution so that the WidgetController does not continue
} else {
return $this->_redirect('/auth/login');
}
}
}
}
How can I stop controller execution?
I would define the user not being logged in and trying to make an XMLHTTPRequest as an exceptional state and let the error handler deal with it by throwing an exception (which stops dispatching of the current action). That way you are also able to handle other kinds of exceptions that might happen:
class BaseController extends Zend_Controller_Action
{
public function init()
{
if ($this->userNotLoggedIn()) {
if ($this->_request->isXmlHttpRequest()) {
throw new Exception('You are not logged in', 401);
} else {
return $this->_redirect('/auth/login');
}
}
}
}
class ErrorController extends Zend_Controller_Action
{
public function errorAction()
{
$errors = $this->_getParam('error_handler');
$exception = $errors->exception;
if ($this->_request->isXmlHttpRequest()) {
$jsonData = Zend_Json::encode($exception);
$jsonData = Zend_Json::encode(array('error'=> $exception->getMessage()));
$isHttpError = $exception->getCode() > 400 && $exception->getCode();
$code = $isHttpError ? $exception->getCode() : 500;
$this->getResponse()
->setHttpResponseCode($code)
->setBody($jsonData)
->setHeader('Content-Type', 'application/json');
} else {
// Render error view
}
}
}
I can think of many ways to stop the controller at this point in your code.
//now stop controller execution so that the WidgetController does not continue
For one, you can replace that line with this the following:
$this->getResponse()->sendResponse();
exit;
That may not be the cleanest but gets the job done rather nicely. The other option is going to be to change the action of the request in the init and let another action handle it. Replace that line with this:
$this->getRequest()->setActionName('invalid-user');
Because your already inside the dispatcher, it's going to run an action inside your action class whether you want it to or not. Trying to change the request in preDispatch will do nothing to change this dispatch. It's determined at this point to run an action inside your class. So, make an action to handle it.
public function invalidUserAction()
{
$this->_helper->layout->disableLayout();
$this->_helper->viewRenderer->setNoRender();
}
For more information see Zend_Controller_Dispatcher_Standard::dispatch.

Resources