Re-init application laravel - laravel-5.7

I have two websites in the one laravel app. Http requests have no problems: depending of domain app include different views, config and other. But commands and async jobs have problems. Default app create. I transmit parameter (domain), but can't re-init app. I do anything, but pathes are default.
$app = require DIR.'/../bootstrap/app.php'; $app->make(Kernel::class)->bootstrap();
I tried, but it not helped.
Tell me please how do it.

I hope that it will be helpful for anybody. I wrote this helper:
/**
* Init the application.
*
* #param string $domain
* #return void
*/
function initApplication(string $domain = App::DOMAINS[App::DEFAULT_SUBDIR])
{
if (function_exists('putenv')) {
array_map('putenv', array_keys($_ENV));
}
$_SERVER = array_diff_key($_SERVER, $_ENV);
$_ENV = [];
global $app;
$_SERVER['SERVER_NAME'] = $domain;
$app = require __DIR__.'/../bootstrap/app.php';
(new Dotenv\Loader($app->environmentFilePath()))->load();
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
}

Related

behat 3 mink testing redirection

In symfony 3 controller file I have function:
/**
* #Route("/user/registration", name="post_registration")
* #Method("POST")
* #return mixed
*/
public function postRegistration()
{
$post = $this->getAllPost();
$this->curl = $this->get('ApiClient');
$responseArray = $this->curl->post(
$this->container->getParameter('api_url') . '/users',
$post
);
if (isset($responseArray['api_key'])) {
return $this->redirectResponseWithCookie($responseArray['api_key']);
} else {
return $this->render(
static::REGISTRATION_TEMPLATE,
['errors' => $responseArray['errors']]
);
}
}
In one part it calls function
redirectResponseWithCookie()
which should redirect the page.
I want to test redirection header - does it have the right Location value.
I have function in UserRegistrationContext class
class UserRegistrationContext extends BaseContext implements Context, SnippetAcceptingContext
{
/**
* #When I register successfully into the system
* #return null
*/
public function iRegisterSuccessfullyIntoTheSystem()
{
$this->canIntercept();
$this->session->getDriver()->getClient()->followRedirects(false);
// Enters his data
// Posts to www.notification.guru .
$this->session->getDriver()->getClient()
->request('POST', $this->baseUrl . '/user/registration', $this->getFormArray());
echo 'test';
}
}
BaseContext class just has some helper functions, its contructor inits session:
$driver = new GoutteDriver();
$this->session = new Session($driver);
this part might be wrong:
$this->session->getDriver()->getClient()->followRedirects(false);
I just took code from behat 2.5 and tried to adjust to work it with behat 3, but not sure if its correct, but at least does not throw error.
It should stop redirect, so then I could get a response header with
getResponseHeaders()
But the problem is that it tries to redirect, and code fails, because real site is not lauched yet where it redirects. And also I would not be able to test headers I guess after real redirection.
So the redirection has to be stopped I guess.
How to do that? I cannot find info.
Test fails at line
$this->session->getDriver()->getClient()
->request('POST', $this->baseUrl . '/user/registration', $this->getFormArray());
So code in my question is not wrong, as I said in comment - I did not see well in console. Often happens that when I post to SO, I do not even finish writing the question, just from writing the details to others I see the answer.
$this->session->getDriver()->getClient()->followRedirects(false);
works well.
So to finish how to check - make function something like this and call it:
/**
* #param Behat\Mink\Session $session - mink session
* #throws \Exception
* #return null
*/
public function isLoggedIntoApi($session)
{
$headers = $session->getResponseHeaders();
if ($headers['Location'][0] != $this->appUrl) {
throw new \Exception(
'User is not redirected to ' . $this->appUrl
);
}
}

ReferenceRepository strips related entities when calling getReference()

I am writing a Symfony 2 unit test that relies heavily on data fixtures. As a shortcut, I wired up a method that will give me access to the fixture loader's ReferenceRepository so that I can access shared entities in my tests.
However, when I pull an object out of the ReferenceRepository, it has no relations, even though I persist them in the data fixture.
The weird part is, there is some code in ReferenceRepository that appears to be stripping those relations out, and I don't understand why it is doing this (let alone how to prevent it).
As an example, here is what a data fixture looks like:
public function load(ObjectManager $manager)
{
$project = new Project();
// ... populate fields ...
/* Add one detail field to the Project. */
$detail = new ProjectDetail();
// ... populate fields ...
$project->addDetail($detail);
$manager->persist($project);
$manager->flush();
$this->addReference('project-onedetail', $project);
}
In my test case, I am doing something (more or less) like this:
$project =
$this->fixtureLoader->getReferenceRepository()
->getReference('project-onedetail');
When I call the method in the test case to grab this Project object, I notice some weird behavior:
From Doctrine\Common\DataFixtures\ReferenceRepository (comments added):
public function getReference($name)
{
$reference = $this->references[$name];
// At this point, $reference contains the Project object with related ProjectDetail.
// It would be awesome if the method would just return $reference...
$meta = $this->manager->getClassMetadata(get_class($reference));
$uow = $this->manager->getUnitOfWork();
if (!$uow->isInIdentityMap($reference) && isset($this->identities[$name])) {
// ... but instead it goes into this conditional....
$reference = $this->manager->getReference(
$meta->name,
$this->identities[$name]
);
// ... and now $reference->getDetails() is empty! What just happened??
$this->references[$name] = $reference; // already in identity map
}
return $reference;
}
What's going on in ReferenceRepository->getReference()? Why are the related objects getting removed from $reference, and how do I prevent that?
What's Going On
After the fixture loader runs, it clears out the UnitOfWork's identity map.
See \Doctrine\Common\DataFixtures\Executor\AbstractExecutor:
public function load(ObjectManager $manager, FixtureInterface $fixture)
{
...
$fixture->load($manager);
$manager->clear();
}
As a result, the condition !$uow->isInIdentityMap($reference) in ReferenceRepository->getReference() will always evaluate to false after the fixture loader has finished.
The Workaround
You can work around this by clearing out ReferenceRepository->$identities. Unfortunately, you don't have direct access to this array, so you'll need to do something slightly kludgy like:
/* #kludge The fixture loader clears out its UnitOfWork object after
* loading each fixture, so we also need to clear the
* ReferenceRepository's identity map.
*/
$repository = $this->fixtureLoader->getReferenceRepository();
$identities = array_keys($repository->getIdentities());
foreach($identities as $key)
{
$repository->setReferenceIdentity($key, null);
}
However, if you do that, you may run into some nasty ORMInvalidArgumentExceptions if you set related objects in your test fixtures:
Doctrine\ORM\ORMInvalidArgumentException: A new entity was found through the relationship '...' that was not configured to cascade persist operations for entity: url. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
The Solution
Ultimately, if you want this to work properly, you'll need to change the behavior of the fixture executor that you use in your test cases so that it does not clear the manager after loading fixtures:
/** Executes data fixtures for unit tests.
*/
class TestExecutor extends ORMExecutor
{
/** Load a fixture with the given persistence manager.
*
* #param ObjectManager|EntityManager $manager
* #param FixtureInterface $fixture
*/
public function load(ObjectManager $manager, FixtureInterface $fixture)
{
/** #kludge Unfortunately, we have to copy-paste a bit of code.
*
* The only difference between this method and AbstractExecutor->load()
* is that we don't call $manager->clear() when we're done loading.
*/
if($this->logger)
{
$prefix = '';
if($fixture instanceof OrderedFixtureInterface)
{
$prefix = sprintf('[%d] ', $fixture->getOrder());
}
$this->log('loading ' . $prefix . get_class($fixture));
}
// additionally pass the instance of reference repository to shared fixtures
if($fixture instanceof SharedFixtureInterface)
{
$fixture->setReferenceRepository($this->referenceRepository);
}
$fixture->load($manager);
/* Do NOT clear the unit of work; we will keep managed entities so that
* they are available to tests.
*/
}
}

Is it possible to add custom routes during compilation passes?

I prepare external bundle and I would like to add some routes during compilation passes.
Routes will be created on the main app/config/config.yml settings.
I was trying to get router from ContainerBuilder in my CustomCompilerPass via:
$definition = $container->getDefinition('router');
, but I got The service definition "router" does not exist.
Is it possible to add custom routes during compilation passes?
There's no way to add routes at compiler passes.
In order to dynamicly load routes (aware of container parameters) I'd use a custom route loader as given in my previous example
class MyLoader extends Loader
{
protected $params;
public function __construct($params)
{
$this->params = $params;
}
public function supports($resource, $type = null)
{
return $type === 'custom' && $this->params == 'YourLogic';
}
public function load($resource, $type = null)
{
// This method will only be called if it suits the parameters
$routes = new RouteCollection;
$resource = '#AcmeFooBundle/Resources/config/dynamic_routing.yml';
$type = 'yaml';
$routes->addCollection($this->import($resource, $type));
return $routes;
}
}
routing.yml
_custom_routes:
resource: .
type: custom
router is an alias, not a service. To get that from a ContainerBuilder, use ContainerBuilder::getAlias. To get the service ID, you need to cast that object to a string: (string) $container->getAlias('router'). Now, you can use that ID to get the service: $container->getDefinition($container->getAlias('router')). And then you get the Service which you can modify to add routes.
BTW, I'm not sure if this is really the thing you want. What about using the CmfRoutingBundle. Then, you use the Chain Router, so you can use both the Symfony2 router and the DynamicRouter. The DynamicRouter can be used with a custom route provider, in which you return the routes you want (you can get them from every resource you want).

Migrations fail when SQLite database file does not exist?

It seems that migrations (sort of) fail silently when the database file does not exist. The migration executes but no db file is created and I can run the migration again. (It never says "nothing to migrate") If I create a blank file then it works.
This is odd because I thought SQLite always created the db file if it was not found so I'm not sure if this is a bug or something I've done wrong. Maybe it's a permissions problem? But everything else is working so I don't know. I'm using Windows 7 and the project is in my
User blamh suggested to add the following snippet to app/start/artisan.php to automatically recreate the database when it doesn't exist, instead of throwing an exception.
if (Config::get('database.default') === 'sqlite') {
$path = Config::get('database.connections.sqlite.database');
if (!file_exists($path) && is_dir(dirname($path))) {
touch($path);
}
}
With this, you can safely delete the SQLite database and then re-migrate and re-seed it, if you wish.
I've issued this bug against laravel/framework.
Hopefully future versions will give an error if the database doesn't exist, or automatically create one.
This is an updated and more flexible solution from Virtlinks answer
<?php
namespace App\Providers;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\ServiceProvider;
class SqliteServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
if (DB::getDriverName() === 'sqlite') {
$path = DB::getConfig('database');
if (!file_exists($path) && is_dir(dirname($path))) {
touch($path);
}
}
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}
Here's yet another way to automatically create the database file, tested on Laravel 5.4.
This is the same as Gummibeer's answer except that I moved the logic to the App\Console\Kernel class (app/Console/Kernel.php), and the check will be performed only when running the migrate command.
<?php
use Illuminate\Support\Facades\DB;
class Kernel extends ConsoleKernel
{
/**
* #param \Symfony\Component\Console\Input\InputInterface $input
* #param \Symfony\Component\Console\Output\OutputInterface $output
* #return int
*/
public function handle($input, $output = null)
{
$this->touchSQLiteDatabase($input);
return parent::handle($input, $output);
}
protected function touchSQLiteDatabase($input)
{
$this->bootstrap();
if (substr((string)$input, 0, 7) == 'migrate' && DB::getDriverName() === 'sqlite') {
$path = DB::getConfig('database');
if (!file_exists($path) && is_dir(dirname($path))) {
touch($path);
}
}
}
}

Symfony2: creating a new SecurityIdentity

I'm using ACL in Symfony 2.1, and I need to create a new SecurityIdentity, so that my ACL can be set in function of some sort of groups.
Picture the following situation: there are groups with users (with different roles) that each have user information. In group 1, users with the ROLE_ADMIN can't edit other users from the same group's information, but in group 2, users with ROLE_ADMIN can edit others information.
So basically my ACL will vary in function of what group the user is in.
I thought I'd start solving this problem with the creation of a new "GroupSecurityIdentity". However the class itself doesn't suffice, as I get this exception when I use it:
$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.
My question is: how do I "register" my new SecurityIdentity so I can use it as RoleSecurityIdentity and UserSecurityIdentity?
What better ways are there to implement a system similar to this I want to do?
2 years ago I went down that path, it turned out to be a bad decision. Modifying the ACL system is difficult and might cause problems when updating Symfony. There are at least 2 better solutions. I'll list them all so you can decide which best suits your needs.
New security identity
I'm using the GroupInterface from FOSUserBundle, but I guess you could use your own too. The following files need to be added:
AclProvider.php
The method to change is private - the whole file has to be copied, but the only change has to be made to hydrateObjectIdentities
GroupSecurityIdentity.php
MutableAclProvider.php
We have to duplicate the whole file as it must extend AclProvider, but we're using a custom one and can't therefore extend the stock MutableAclProvider. The methods changed are getInsertSecurityIdentitySql and getSelectSecurityIdentityIdSql.
SecurityIdentityRetrievalStrategy.php
Next up: rewire the dependency injection container by providing the following parameters:
<parameter key="security.acl.dbal.provider.class">
Acme\Bundle\DemoBundle\Security\Acl\Dbal\MutableAclProvider
</parameter>
<parameter key="security.acl.security_identity_retrieval_strategy.class">
Acme\Bundle\DemoBundle\Security\Acl\Domain\SecurityIdentityRetrievalStrategy
</parameter>
Time to cross fingers and see whether it works. Since this is old code I might have forgotten something.
Use roles for groups
The idea is to have group names correspond to roles.
A simple way is to have your User entity re-implement UserInterface::getRoles:
public function getRoles()
{
$roles = parent::getRoles();
// This can be cached should there be any performance issues
// which I highly doubt there would be.
foreach ($this->getGroups() as $group) {
// GroupInterface::getRole() would probably have to use its
// canonical name to get something like `ROLE_GROUP_NAME_OF_GROUP`
$roles[] = $group->getRole();
}
return $roles;
}
A possible implementation of GroupInterface::getRole():
public function getRole()
{
$name = $this->getNameCanonical();
return 'ROLE_GROUP_'.mb_convert_case($name, MB_CASE_UPPER, 'UTF-8');
}
It's now just a matter of creating the required ACE-s as written in the cookbook article.
Create a voter
Finally, you could use custom voters that check for the presence of specific groups and whether the user has access to said object. A possible implementation:
<?php
namespace Acme\Bundle\DemoBundle\Authorization\Voter;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
class MySecureObjectVoter implements VoterInterface
{
/**
* {#inheritDoc}
*/
public function supportsAttribute($attribute)
{
$supported = array('VIEW');
return in_array($attribute, $supported);
}
/**
* {#inheritDoc}
*/
public function supportsClass($class)
{
return $class instanceof GroupableInterface;
}
/**
* {#inheritDoc}
*/
public function vote(TokenInterface $token, $object, array $attributes)
{
$result = VoterInterface::ACCESS_ABSTAIN;
if (!$object instanceof MySecureObject) {
return VoterInterface::ACCESS_ABSTAIN;
}
foreach ($attributes as $attribute) {
if (!$this->supportsAttribute($attribute)) {
continue;
}
// Access is granted, if the user and object have at least 1
// group in common.
if ('VIEW' === $attribute) {
$objGroups = $object->getGroups();
$userGroups = $token->getUser()->getGroups();
foreach ($userGroups as $userGroup) {
foreach ($objGroups as $objGroup) {
if ($userGroup->equals($objGroup)) {
return VoterInterface::ACCESS_GRANTED;
}
}
}
return voterInterface::ACCESS_DENIED;
}
}
}
}
For more details on voters please refer to the cookbook example.
I would avoid creating a custom security identity. Use the two other methods provided. The second solution works best, if you will be having lots of records and each of them must have different access settings. Voters could be used for setting up simple access granting logic (which most smaller systems seem to fall under) or when flexibility is necessary.
I write my answer here to keep a track of this error message.
I implemented group support with ACL and i had to hack a bit the symfony core "MutableAclProvider.php"
protected function getSelectSecurityIdentityIdSql(SecurityIdentityInterface $sid)
{
if ($sid instanceof UserSecurityIdentity) {
$identifier = $sid->getClass().'-'.$sid->getUsername();
$username = true;
} elseif ($sid instanceof RoleSecurityIdentity) {
$identifier = $sid->getRole();
$username = false;
}else {
//throw new \InvalidArgumentException('$sid must either be an instance of UserSecurityIdentity, or RoleSecurityIdentity.');
$identifier = $sid->getClass().'-'.$sid->getGroupname();
$username = true;
}
return sprintf(
'SELECT id FROM %s WHERE identifier = %s AND username = %s',
$this->options['sid_table_name'],
$this->connection->quote($identifier),
$this->connection->getDatabasePlatform()->convertBooleans($username)
);
}
Even if the provided object is not an instance of UserSecurityIdentity or RoleSecurityIdentity it return a value. So now i can use a custom "GroupSecurityIdentity"
It's not easy to put in place but was much adapted to my system.

Resources