In Laravel 8 app using spatie/laravel-permission ^3.18 I
defined in bootstrap/app.php
if ( ! defined("PERMISSION_USE_SERVICES")) {
define("PERMISSION_USE_SERVICES", 'Use services');
}
and in init seeder :
$CustomerRole = Role::create(['name' => ROLE_CUSTOMER, 'guard_name' => 'web']);
$customerUseServicesPermission = Permission::create(['name' => PERMISSION_USE_SERVICES, 'guard_name' => 'web']);
$CustomerRole->givePermissionTo($customerUseServicesPermission); // means : customer use services
and when I register new user in app/Actions/Fortify/CreateNewUser.php I want to give new user
customerUseServicesPermission.
In vendor/spatie/laravel-permission/src/Contracts/Permission.php I found method :
interface Permission
{
public static function findByName(string $name, $guardName): self;
But using it I got error :
Cannot call abstract method Spatie\Permission\Contracts\Permission::findByName()
have I to make wrapper file for Contracts/Permission.php and in which way ?
Thanks!
Related
Is it possible to catch external requests in Laravel Telescope. I'm new to telescope and I've done my research but I couldn't find any blog/article that mentioned this except this but it didn't work for me
I've Installed telescope on my app according to the documentation, Iv'e created a new watcher called GuzzleRequestWatcher and registered it under config/telescope.php, I've also created a test route that sends an http::post message to this. Telescope is catching my API request and recording it under requests as shown in the screenshot but I need it to see the URL the request is hitting not only the route for example rather than showing in Path '/api/v1/guzzle-test' I need it to show the URL I'm requesting 'http://httpbin.org/anything'.screenshot
<?php
declare(strict_types=1);
namespace App\Telescope\Watchers;
use Closure;
use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Log;
use Laravel\Telescope\IncomingEntry;
use Laravel\Telescope\Telescope;
use Laravel\Telescope\Watchers\FetchesStackTrace;
use Laravel\Telescope\Watchers\Watcher;
final class GuzzleRequestWatcher extends Watcher
{
use FetchesStackTrace;
public function register($app)
{
$app->bind(Client::class, $this->buildClient($app));
}
private function buildClient(Application $app): Closure
{
return static function (Application $app): Client {
$config = $app['config']['guzzle'] ?? [];
if (Telescope::isRecording()) {
$config['on_stats'] = function (TransferStats $stats) {
$caller = $this->getCallerFromStackTrace();
Telescope::recordQuery(
IncomingEntry::make([
'connection' => 'guzzle',
'bindings' => [],
'sql' => (string) $stats->getEffectiveUri(),
'time' => number_format(
$stats->getTransferTime() * 1000,
2,
''
),
'slow' => $stats->getTransferTime() > 1,
'file' => $caller['file'],
'line' => $caller['line'],
'hash' => md5((string) $stats->getEffectiveUri()),
])
);
};
}
return new Client(
$config
);
};
}
}
I am using this ("browserstack/browserstack-local": "^1.1") package to run dusk tests on BrowserStack. Now the requirement is to run tests on multiple and different devices with different browsers. Currently, I am following this approach to run tests.
private function browserStackCaps($local_identifier)
{
return [
'project' => config('app.name'),
'browserstack.local' => 'true',
'browser' => env('BROWSER'),
'device' => env('DEVICE'),
'acceptSslCert' => true,
'resolution' => '1920x1080'
];
}
The drawback of this approach is I have to change the device name and browser name in the .env file every time I need to run tests on a different device/browser. Is there any way I can run tests on the provided array? The array that contains devices and browser information.
I know this is old, but I found this page while searching for a solution. I ended up building one myself that would probably meet your use-case. The biggest hurdle that I had was $this->browse() in a normal Dusk test was using a single instance of Laravel\Dusk\Browser and the new capabilities were not being pulled in. This implementation adds a function called performTest to the DuskTestCase.php file. This function loops through a set of capabilities and instantiates a new instance of Laravel\Dusk\Browser for each test. This function works similarly to the existing browse function in Laravel Dusk. You call performTest by passing it a callable that accepts a single parameter which is an instance of Laravel\Dusk\Browser
Dusk Test Case
<?php
namespace Tests;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
protected array $capabilities;
private const BROWSERS = [
'ios_14_iphone_xs_safari' => [
"os_version" => "14",
"device" => "iPhone XS",
"real_mobile" => "true",
"browserstack.local" => "true",
'acceptSslCerts' => 'true'
],
'mac_osx_catalina_safari' => [
"os" => "OS X",
"os_version" => "Catalina",
"browser" => "Safari",
"browser_version" => "13.0",
"browserstack.local" => "true",
"browserstack.selenium_version" => "3.14.0",
"resolution" => "1920x1080",
'acceptSslCerts' => 'true',
]
];
/**
* Create the RemoteWebDriver instance.
*
* #return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver()
{
$browserStackConnectionUrl = config('browserstack.connection_url');
return RemoteWebDriver::create(
$browserStackConnectionUrl, $this->capabilities
);
}
protected function performTest(Callable $test){
foreach(self::BROWSERS as $browserName => $capabilitySet){
try {
$this->capabilities = $capabilitySet;
$browser = $this->newBrowser($this->driver());
$test($browser);
$browser->quit();
fprintf(STDOUT, "\e[0;32m√ {$browserName}\r\n");
}
catch(\Exception $exception){
fprintf(STDOUT, "\e[0;31mX {$browserName}\r\n");
throw $exception;
}
}
}
}
Example Test
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Laravel\Dusk\Browser;
class ExampleTest extends DuskTestCase
{
public function testExample()
{
$this->performTest(function(Browser $browser){
$browser->visit('/')
->assertDontSee('Foobar');
});
}
}
config/browserstack.php
<?php
return [
'connection_url' => env('BROWSERSTACK_CONNECTION_URL')
];
you can implement this at your end. Fetch the list of Browsers and Devices you want to execute your tests on using the REST API and use the same.
REST API to be used:
curl -u "username:password"
https://api.browserstack.com/automate/browsers.json
Read more on this here:
https://www.browserstack.com/docs/automate/api-reference/selenium/browser#get-browser-list
I have to convert my unit tests to codeception. I need to use loginWithFakeUser() function from this article - How to mock authentication user on unit test in Laravel?
public function loginWithFakeUser() {
$user = new User([
'id' => 1,
'name' => 'yish'
]);
$this->be($user);
}
How can I use the $this->be() when my class is already extending \Codeception\Test\Unit? I don't know what should I use .. or how to use properly. Putting the function loginWithFakeUser() inside this:
use Illuminate\Foundation\Testing\Concerns\InteractsWithAuthentication;
use Illuminate\Foundation\Testing\Concerns\InteractsWithSession;
class AdminTest extends \Codeception\Test\Unit {
use InteractsWithAuthentication;
use InteractsWithSession;
}
Gives me an error:
[ErrorException] Undefined property: AdminTest::$app
I'm not sure how can I set the $app variable. Please help me. Thanks a lot!
I was able to solve this by mocking the Auth class.
$oUser = new User([
'id' => 1,
'name' => 'yish'
]);
Auth::shouldReceive('check')->once()->andReturn(true);
Auth::shouldReceive('user')->once()->andReturn($oUser);
Where in my actual code it uses it as:
if(Auth::check() === true) {
$sName = Auth::user()->name;
}
I have Eloquent working outside of Laravel with no problems. Now Im trying to use also the Validation class by:
<?php
namespace User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Validator;
class User extends Model {
private $rules = array(
'firstName' => 'required|min:2|max:50',
'lastName' => 'required|min:2|max:50',
);
public function validate($data)
{
$v = Validator::make($data, $this->rules);
return $v->passes();
}
}
Executing that code give me an error:
Call to undefined method Illuminate\Validation\Validator::make()
That is correct since the method make is not on the class Validator but in his factory:
How can I correctly instantiate the Validation lib in order to get this working?
BTW, this is my composer.json:
{
"require": {
"slim/slim": "^2.6",
"illuminate/database": "^5.1",
"illuminate/validation": "^5.1"
},
"autoload": {
"classmap": [
"app/model"
]
}
}
To get the validation package to work outside of Laravel, you'll also need the translation package.
So first create an instance of the translator, and then use that to make a validation factory.
Working example
<?php
/*
Required composer packages:
illuminate/validation
illuminate/translation
*/
/*
Translation language files directory is the same as with Laravel
./lang/en/validation.php
*/
require_once 'vendor/autoload.php';
// You need to specify where the translation files is
$test_translation_path = __DIR__.'/lang';
$test_translation_locale = 'en';
// Set up data for the validator
$test_input_data = ['field' => 'value'];
$test_input_rules = ['field' => 'required'];
$translation_file_loader = new Illuminate\Translation\FileLoader(new Illuminate\Filesystem\Filesystem, $test_translation_path);
$translator = new Illuminate\Translation\Translator($translation_file_loader, $test_translation_locale);
$validation_factory = new Illuminate\Validation\Factory($translator);
$validator = $validation_factory->make($test_input_data, $test_input_rules);
if ($validator->fails()) {
die('Validation failed');
}
die('Validation passed!');
There are a few issues with your approach:
you're trying to create a new Validator instance by calling the Illuminate\Validation\Validator::make method, yet you point out that the make() method is present on the Illuminate\Validation\Factory which is a different class altogether, so the error you're getting is justified.
you're trying to call the make() method statically :: when in fact it's not defined as such.
you're trying to use the Validator the same as you would in a Laravel application enviroment, which won't work because you're missing the Laravel Facades and Service Providers infrastructure that Laravel uses to allow for such a simple instantiation of the Validator.
If you were to look at the registerValidationFactory() method inside the Illuminate\Validation\ValidationServiceProvider class, you'd get a sense of how the validator instance is created. So based on that, you could do the following:
namespace User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Validation\Factory as ValidatorFactory;
use Symfony\Component\Translation\Translator;
class User extends Model {
private $rules = array(
'firstName' => 'required|min:2|max:50',
'lastName' => 'required|min:2|max:50',
);
public function validate($data)
{
$factory = new ValidatorFactory(new Translator('en'));
$v = $factory->make($data, $rules);
return $v->passes();
}
}
I found how to get a session container like this:
$session = new \Zend\Session\Container('base');
But what if I need to access the session in many places during processing a HTTP request.
Let's say in the Application module's indexAction in the IndexController, then I redirect it to the User\Controller\IndexController and need to access the session again, and then in a view helper or two, and who knows how often more.
When constructing the session container every time anew, that is a waste of processing time. Yes, I debugged it to see what's going on in the constructor, and yes, there is some code executed behind the scenes. It is not as if the constructor would just return a global variable or something else which would be immutable and doesn't need a construction process.
So what to do?
Should I create a service for it?
a controller plugin?
a view helper?
a service and a controller plugin and a view helper, with the latter calling the service?
I'm sure it is something that many people must have come across and have dealt with, but I can't find any information on this.
Any hint is dearly appreciated.
Many thanks in advance! :-)
Here's a more refined and improved version.
It consists of the service "SessionService", a ViewHelper (which calls the SessionService), a ControllerPlugin (which also calls the SessionService), and shows how to set them up in the configuration file "module.config.php".
Make sure you set "use" paths or use absolute class paths in config.
SessionService.php:
class SessionService
{
protected $sessionContainer;
public function setSessionContainer(
$sessionContainer
) {
$this->sessionContainer = $sessionContainer;
}
public function __invoke() {
return $this->sessionContainer;
}
}
SessionHelper.php:
class SessionHelper extends \Zend\View\Helper\AbstractHelper
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
SessionPlugin.php:
class SessionPlugin extends AbstractPlugin
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function(
ServiceLocatorInterface $serviceLocator
) {
$sessionContainer = new \Zend\Session\Container('base');
$sessionService = new SessionService();
$sessionService->setSessionContainer($sessionContainer);
return $sessionService;
},
),
),
'controller_plugins' => array(
'factories' => array(
'sessionPlugin' => function(
AbstractPluginManager $pluginManager
) {
$sessionService = $pluginManager->getServiceLocator()->get('sessionService');
$sessionPlugin = new SessionPlugin();
$sessionPlugin->setSessionService($sessionService);
return $sessionPlugin;
},
),
),
'view_helpers' => array(
'factories' => array(
'sessionHelper' => function (
AbstractPluginManager $helperPluginManager
) {
$sessionService = $helperPluginManager->getServiceLocator()->get('sessionService');
$sessionHelper = new SessionHelper();
$sessionHelper->setSessionService($sessionService);
return $sessionHelper;
},
),
),
In your Controller write:-
use Zend\Session\Container;
Make Session variable
$user_session = new Container('user');
'user' is Your Session Name To put Value in Your Session write:
$user_session->username = 'xyz';
After Storing You can Access Your Session By:
$user_session-> username
To destroy Session Variable Use:
$session = new Container('user');
$session->getManager()->getStorage()->clear('user');
it is Just Like : -
unset($_SESSION['user']);
http://wownewcode.blogspot.in/2013/12/set-session-in-zend-framework-2.html
Once you've initialized a session Container you can just re-use it with $_SESSION['container_name'];
Basically $session = new \Zend\Session\Container('base'); will create an ArrayObject named base inside $_SESSION. One advantage of initializing by creating a Container is that you can specify a TTL or expiration after x-hops.
$_SESSION['base']['key'] = "store this value";
I think you need to use session Manager and service with the container storage to achieve your goal.
you can set it in your application Module
Application\Module.php
use Zend\Session\Config\SessionConfig;
public function onBootstrap(EventInterface $e)
{
//Your other code here
//configure session
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
}
and in module.config.php
'session' => array(
'save_path' => realpath(ZF2PROJECT_ROOT . '/data/session'),
'name' => 'ZF2PROJECT_SESSION',
),
and in your services you can use like this. Forexample in Authentication service.
class AuthenticationService
{
protected $storage = null;
public function getStorage()
{
if (null === $this->storage) {
$this->setStorage(new Storage\Session());
}
return $this->storage;
}
}
Here is my current provisional solution or workaround, consisting of:
- a service for storing the session container object.
- a controller plugin for easy access to the session container, without having to inject a dependency for it in every controller.
The session service:
class Session
{
private static $container;
public function getContainer() {
if (!isset(self::$container)) {
self::$container = new \Zend\Session\Container('base');
}
return self::$container;
}
public function __invoke() {
return $this->getContainer();
}
}
The controller plugin:
class Session extends AbstractPlugin
{
protected $sessionService;
public function __construct(
SessionService $sessionService
) {
$this->sessionService = $sessionService;
}
public function getContainer() {
return $this->sessionService->getContainer();
}
public function __invoke() {
return $this->getContainer();
}
}
Configuration in module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function($sm) {
return new Application\Service\Session\Session();
},
),
),
'controller_plugins' => array(
'factories' => array(
'session' => function($serviceLocator) {
$sessionService = $serviceLocator->get('sessionService');
return new Application\Service\Mvc\Controller\Plugin\Session($sessionService);
},
),
),
Usage example in any controller or controller plugin method:
$sessionContainer = $this->session->getContainer();
or short form (because session service and controller plugin both implement __invoke):
$sessionContainer = $this->session();
and then can use the session container to store any variables in it, like this:
$sessionContainer->foo = 'bar';
Because the session service is created by a factory function through module.config.php, it is only created once.
The actual session container is a static variable in the session service and only created once, i.e. if it doesn't exist.
In subsequent calls to the getSessionContainer function, this only once created static session container is returned.
This is just a provisional workaround solution, works for me for now, but for making it re-usable for other applications also, it should provide functions to customize the session container name and the storage place and strategy, those parts are missing in this simple workaround solution.
Note: A view helper should not be necessary for it. Session variables should not be set in a view, and if a view needs read access to them, the data should be passed via a view model from controller to view.