Call a Controller method from a Command in Laravel - php

I have a Command that is listening via Redis Pub/Sub. When a Publish is received, I want to call a controller method so that I can update the database.
However, I have not been able to find any solution on how to call a controller method with parameters from inside of the project but outside of the routes. The closest thing I have seen is something like:
return redirect()->action(
'TransactionController#assignUser', [
'transId' => $trans_id,
'userId' => $user_id
]);
My complete command that I've tried looks like this:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Redis;
class RedisSubscribe extends Command
{
protected $signature = 'redis:subscribe';
protected $description = 'Subscribe to a Redis channel';
public function handle()
{
Redis::subscribe('accepted-requests', function ($request) {
$trans_array = json_decode($request);
$trans_id = $trans_array->trans_id;
$user_id = $trans_array->user_id;
$this->assignUser($trans_id, $user_id);
});
}
public function assignUser($trans_id, $user_id)
{
return redirect()->action(
'TransactionController#assignUser', [
'transId' => $trans_id,
'userId' => $user_id
]);
}
}
However, this does not seem to work. When I run this Command, I get an error that assignUser() cannot be found (even though it exists and is expecting two paramters). I am also not sure a "redirect" is really what I am after here.
Is there some other way to call a controller function in a Command, or some other way that would make this possible to do?

If your controller does not have any required parameters, you can just create the controller as a new object, and call the function.
$controller = new TransactionController();
$controller->assignUser([
'transId' => $trans_id,
'userId' => $user_id
]);

Related

Factory relationship returning null laravel testing

I'm trying to unit testing a service that handles the registration of a user in Laravel.
This is the service:
public function completeRegistration(Collection $data)
{
$code = $data->get('code');
$registerToken = $this->fetchRegisterToken($code);
DB::beginTransaction();
$registerToken->update(['used_at' => Carbon::now()]);
$user = $this->userRepository->update($data, $registerToken->user);
$token = $user->createToken(self::DEFAULT_TOKEN_NAME);
DB::commit();
return [
'user' => $user,
'token' => $token->plainTextToken,
];
}
Where the update method has the following signature:
<?php
namespace App\Repositories\User;
use App\Models\User;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
interface UserRepositoryInterface
{
public function create(Collection $data): User;
public function update(Collection $data, User $user): User;
}
With my test being:
/**
* Test a user can register
*
* #return void
*/
public function test_user_can_complete_registration()
{
$userRepositoryMock = Mockery::mock(UserRepositoryInterface::class);
$registerTokenRepositoryMock = Mockery::mock(RegisterTokenRepository::class);
$userFactory = User::factory()->make();
$registerTokenFactory = RegisterToken::factory()
->for($userFactory)
->timestamped()
->make(['user_id' => $userFactory->id]);
dd($registerTokenFactory->user);
$userRepositoryMock
->expects($this->any())
->once()
->andReturn($userFactory);
....
}
When I run phpunit --filter=test_user_can_complete_registration I get the following error:
1) Tests\Unit\Services\Auth\AuthServiceTest::test_user_can_complete_registration
TypeError: Argument 2 passed to Mockery_0_App_Repositories_User_UserRepositoryInterface::update() must be an instance of App\Models\User, null given, called in /var/www/app/Services/Auth/AuthService.php on line 64
/var/www/app/Services/Auth/AuthService.php:64
/var/www/tests/Unit/Services/Auth/AuthServiceTest.php:88
This tells me that the user relationship on $registerTokenFactory is null. When I do:
public function test_user_can_complete_registration()
{
...
dd($registerTokenFactory->user);
}
I get the output null. I'm trying to test the service without hitting the database. How can I attach the user relationship to the $registerTokenFactory object? I have tried using for and trying to attach directly:
$registerTokenFactory = RegisterToken::factory()
->for($userFactory)
->timestamped()
->make(['user_id' => $userFactory->id, 'user' => $userFactory]);
In Laravel factories make() does only create the model and does not save it. For relationship to work, you will need your models to be saved.
$userFactory = User::factory()->create();
Since you do not want to use a Database, which is wrong in my opinion. People don't like writing tests, so when we have to do it make it simple, mocking everything to avoid databases is a pain. Instead an alternative is to you Sqlite to run in memory, fast and easy. A drawback is some functionality does not work there JSON fields and the version that are in most Ubuntu distributions does not respect foreign keys.
If you want to follow the path you are already on, assigned the user on the object would work, you have some left out bits of the code i assume.
$userRepositoryMock = Mockery::mock(UserRepositoryInterface::class);
$registerTokenRepositoryMock = Mockery::mock(RegisterTokenRepository::class);
$user = User::factory()->make();
$registerToken = RegisterToken::factory()
->for($userFactory)
->timestamped()
->make(['user_id' => $user->id]);
$registerToken->user = $user;
$registerTokenRepositoryMock
->expects('fetchRegisterToken')
->once()
->andReturn($registerToken);
$userRepositoryMock
->expects($this->any())
->once()
->andReturn($user);
// execute the call

Laravel 6 - Pass a parameter to $app->when()->needs()->give()

I am trying to use this package to push notifications to users via OneSignal. However I needed to make a little change. My API serves two (related) apps and I have two OneSignal configs. I am trying to override its ServiceProvider (using this technique).
The ServiceProvider presents itself as follows
<?php
namespace NotificationChannels\OneSignal;
use Berkayk\OneSignal\OneSignalClient;
use Illuminate\Support\ServiceProvider;
use NotificationChannels\OneSignal\Exceptions\InvalidConfiguration;
class OneSignalServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*/
public function boot()
{
$this->app->when(OneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
}
The behavior that I want to change is located in the line
$oneSignalConfig = config('services.onesignal');
As it assumes that my config/services.php has the following entry (stated in the doc) :
// config/services.php
...
'onesignal' => [
'app_id' => env('ONESIGNAL_APP_ID'),
'rest_api_key' => env('ONESIGNAL_REST_API_KEY')
],
...
Whereas I want to set my config/services.php as follows
// config/services.php
...
'onesignal' => [
'app1' => [
'app_id' => env('ONESIGNAL_1_APP_ID'),
'rest_api_key' => env('ONESIGNAL_1_REST_API_KEY')
],
'app2' => [
'app_id' => env('ONESIGNAL_2_APP_ID'),
'rest_api_key' => env('ONESIGNAL_2_REST_API_KEY')
],
],
...
And I want somehow to tell my ServiceProvider (through some kind of parameter) to either do
$oneSignalConfig = config('services.onesignal.app1');
OR
$oneSignalConfig = config('services.onesignal.app2');
But I didn't find any way to pass a parameter to the class, the boot function or the give method (and if I understood well I shouldn't even be doing that).
The only way I could think of is to create two classes that extend the OneSignalChannel::class
and duplicate code in the boot function so it becomes as follows :
public function boot()
{
$this->app->when(FirstOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app1');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
$this->app->when(SecondOneSignalChannel::class)
->needs(OneSignalClient::class)
->give(function () {
$oneSignalConfig = config('services.onesignal.app2');
if (is_null($oneSignalConfig)) {
throw InvalidConfiguration::configurationNotSet();
}
return new OneSignalClient(
$oneSignalConfig['app_id'],
$oneSignalConfig['rest_api_key'],
''
);
});
}
The difference in the when provoking a difference in the config but it seems a lot of duplication and not extensible (what if I had three apps).
Should I use this method, or is there a way to pass a parameter to this ServiceProvider or is there another solution ?
https://stackoverflow.com/a/34224082/10371024
I could see what you need, but to pass parameter to boot method is not a good idea according to Laravel architecture. You may try to get what you want with using events as Vladislav suggested.

Consuming external Laravel API search variables in to my environment file

I have two laravel projects, one have an API(laravel_1) and the other consumes it(laravel_2).
I can call the api on Postman without problem or consume api on laravel_1 using guzzle call, but when I do the same on laravel_2 it throws error of databases if I try to call some eloquent method like
Articles::all(); for example, so I did some tests and seems like laravel_2 try to uses his .env when calling laravel_1.
If I call the laravel_1 api from postman it returns his app name correct "Laravel_1" but if I call it from laravel_2 it's says "Laravel_2" it takes his own app.env name instead of laravel_1 how is that? Also it's the same when I try to call a database
Here is laravel_1 api code:
web.php:
Route::post('/housingAPI/getEventOffers', 'HousingApiController#getEventOffers')->name("housingApi.getEventOffers");
HousingApiController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class HousingApiController extends Controller
{
public function getEventOffers(Request $request)
{
//todo code here with requests
return config('app.name');
}
}
and here is laravel_2 config:
web.php
Route::get('/test', 'HousingInfoController#index')
HousingInfoController.php
<?php
namespace App\Http\Controllers\frontend;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class HousingInfoController extends Controller
{
public function test()
{
//Ofertas de eventos
$eventOffers = $this->getAllEventOffersApiCall("H", 1, 416, "esp", '');
}
private function getAllEventOffersApiCall($type, $zoneId, $hotelId, $lang, $moda, $ajax = false)
{
$form_params = [
'type' => $type,
'zoneId' => $zoneId,
'hotelId' => $hotelId,
'lang' => $lang,
'moda' => $moda,
'ajax' => $ajax
];
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'http://laravel-api.test/housingAPI/getEventOffers', ['form_params' => $form_params]);
$body = $response->getBody();
dd($body->getContents());
return json_decode($body->getContents(), true);
}
}

Laravel 5: Post - Pass Arguments To Controller

I think what I want is quite basic, I just can't find the proper syntax as I am still learning Laravel.
So, I am using google verification for sign ins on my website. This entails a post request to my backend that has to be handled, I have put this logic in a controller. My routes.php:
Route::post('google' , [
'as' => 'verify.index',
'uses' => 'verify#verifyIdToken'
]);
My controller (verify.php):
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
class verify extends Controller
{
public function verifyIdToken($token)
{
$token = $_POST['id'];
$email = $_POST['email'];
return $this->getAuth()->verifyIdToken($token);
echo $this->getAuth()->verifyIdToken($token);
return view('aviewII')->with(['verify' => json_encode(verifyIdToken($token)),'email'=> json_encode($email)]);
}
}
Of course, because of how the function in the controller is written, I get the following error Missing argument 1 for App\Http\Controllers\verify::verifyIdToken() My question is, how do I tell the function in the controller to take $_POST['id'] as the argument for $token?
Something like this:
Route::post('google' , [
'as' => 'verify.index',
'uses' => 'verify#verifyIdToken ~with $_POST['id'] as $token'
]);
For additional reference, my actual post request looks like this:
$.post( "http://example.com/google", {email:profile.getEmail(),id:id_token} );
Controller method:
public function verifyIdToken(Request $request)
{
// Not necessary but a better practice for ajax 'POST' responses.
if($request->ajax() && $request->isMethod('post'))
{
return $request::get('token');
}
}
Route:
Route::post('google', ['as' => 'some.alias', 'uses' => 'SomeController#verifyIdToken']);
You are looking for Laravel's request class. You should type-hint the class on your method, which then allows loads of options to actually obtain the data. Something like:
use Illuminate\Http\Request;
public function verifyIdToken(Request $request)
{
$token = $request->input('id');
$email = $request->input('email');
return $this->getAuth()->verifyIdToken($token);
echo $this->getAuth()->verifyIdToken($token);
return view('aviewII')->with(['verify' => json_encode(verifyIdToken($token)),'email'=> json_encode($email)]);
}
The documentation on it has tons more useful information.

Redirect is not working on Laravel 4

In my controller function I am using a require statement to include a file:
require app_path().'/plivo/plivo.php';
After this statement, I try to redirect from this controller using the following statement:
return Redirect::back()->with('success', 'Note added successfully');
However, this gives me the following error:
Call to undefined method Redirect::back()
How can I redirect from this function?
This is my full code:
public function sendSMS(){
require app_path().'/plivo/plivo.php';
$auth_id = "XXXXXXXXXXXX";
$auth_token = "XXXXXXXXXXXXXXXXXXXXX";
$p = new \RestAPI($auth_id, $auth_token);
$params = array(
'src' => '1XX7XX0',
'dst' => '91XXXXXXXXX7',
'text' => 'Test SMS',
'method' => 'POST'
);
$response = $p->send_message($params);
return Redirect::back()->with('success', 'Note added successfully');
}
This answer assumes that plivo.php is from this git repo.
The issue is that the plivo.php library defines a Redirect class in the global namespace. Because of this, Laravel does not register the global Redirect alias to point to the Illuminate\Support\Facades\Redirect facade.
So, in your final line return Redirect::back()->with(...);, the Redirect class being used is the class defined in the plivo.php library, not Laravel's Illuminate\Support\Facades\Redirect class.
The quickest fix would be to change your line to:
return Illuminate\Support\Facades\Redirect::back()->with('success', 'Note added successfully');
Another option would be to inject Laravel's redirector into your controller, and use that instead of using the facade:
class MyController extends BaseController {
public function __construct(\Illuminate\Routing\Redirector $redirector) {
$this->redirector = $redirector;
}
public function sendSMS() {
require app_path().'/plivo/plivo.php';
//
return $this->redirector->back()->with('success', 'Note added successfully');
}
}
A third option would be to update your code to use the plivo composer package, which has a namespace. The updates have been done in the dev branch of the repo, which you can find here. If you did this, you would get rid of your require statement and use the namespaced plivo classes.

Categories