Test symfony doctrine UnitOfWork error only during tests - symfony

I have a problem during testing a symfony App with phpunit.
I created a service and it works well but when I execute the service in my tests doctrine throw me an exception
A new entity was found through the relationship 'PointOfSale#country' that was not configured to cascade persist operations for entity
In my service I do this:
$pointOfSale->setCountry($this->getCountry($name));
private function getCountry(string $name): Country
{
$country = $this->entityManager->getRepository(Country::class)->findOneBy(['alpha3Code' => $name]);
return $country;
}
I don't understand why this entity is not managed by UnitOfWorks. Indeed when I do the following code I have an exception only during test
$country = $this->entityManager->getRepository(Country::class)->findOneBy(['alpha3Code' => $name]);
$this->entityManager->refresh($country);
[ERROR] Entity Namespace\Country#000000001bfea0ed00000000602457d6 is not managed. An entity is
managed if its fetched from the database or registered as new through EntityManager#persist
How can I fix this ?

Solution was discuss here:
https://github.com/doctrine/DoctrineBundle/issues/1112#event-3005473120
[doctrine changelog note] (https://github.com/doctrine/DoctrineBundle/blob/1.12.x/UPGRADE-1.12.md#unitofwork-cleared-between-each-request)

While troubleshooting the same problem with PhpUnit Testing producing "A new entity was found through the relationship" errors I realized I was actually getting different Entity Manager instances thanks to this question. What I did was move the self::bootKernel() from my __construct function to a new "startUp" function that I run at beginning of each test function and then I started getting the correct Entity Manager each time instead of a different one each test.
I may be missing something, but now my tests pass and produce the same results as my actual program.

Related

How to access doctrine inside the bundle build method

I have a build method like the following, Inside the bundle class.
public function build(ContainerBuilder $container)
{
$em = $container->get('doctrine')->getManager();
}
And I want to access the doctrine to retrieve some data from the database.
But I get an error You have requested a non-existent service "doctrine". And I don't know what to do. I appreciate any help.
Please notice, that argument of the build() method is of type ContainerBuilder.
ContainerBuilder is meant to collect information about service definitions to allow the actual compilation of the container. Therefore no actual service instances are available in this class. Moreover, it is possible that container itself is not available at all at this stage, for example in the case of a fresh install.
You need to re-consider your decision on the requirement of the Doctrine at this stage. You may post a separate question if you need assistance with finding a better way of implementation of the underlying task which brings you to this question.

Related entities not loaded in Codeception functional tests

I'm using Codeception to test a Symfony 3.4 project with Doctrine2 module enabled.
The problem is that I can't access related entities on an entity I receive by calling grabEntityFromRepository() function. So if I do:
$person = $I->grabEntityFromRepository('AppBundle:Person', ['id' => 1]);
$address = $person->getAddress(); // Address is a one to one related entity to person
The $address variable is NULL.
Is this an expected behaviour or can it be tweaked in the configuration somewhere?

Pass arguments to IDesignTimeDbContextFactory of EF Core

How can we pass arguments to dotnet ef database update?
i want to be able to update different database with the use of arguments.
i've tried
dotnet ef database update "Accept"
dotnet ef databse update Accept
but it didn't work..
Or how I can put a switch to get different conenctionString from my configuration?
public ProjectContext CreateDbContext(string[] args)
{
IConfigurationRoot configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
// Find a way to get different connection string
var connectionString = configuration.GetConnectionString(args[0]);
var builder = new DbContextOptionsBuilder<ProjectContext >();
builder.UseSqlServer(connectionString);
return new ProjectContext(builder.Options);
}
.NET 5 releases in a couple weeks from the time of this answer. So this is possible to change.
Answer Now
The .NET 5, and the associated EF Core 5+ NuGets support this. In Package Manager you can type:
Add-Migration YourMigrationName -Args "Space Separated Args"
Update-Database -Args "Space Separated Args"
For example, I use this:
Update-Database -Args MyConnName
In my Startup project I have a config file (Or appsettings.json) that has that connection string key, and I pull that in.
Note I said .NET Core 5. This will be have a full release in a few weeks from now. So in a few weeks this answer may be simple. But until then, you may need to install Preview versions (And NuGet PreReleases)
Answer Prior to now
There were lacking options when this question was asked, though there were options, like using dotnet ef commands with AppArgs, as discussed here. But these have changed, and are also now accessible from PM Console as discussed in the above "Now" answer.
You can also set variables like this:
$env:SqlConnectionString="Server=tcp:mySqlServerStuffxxx"
Add-Migration InitialCreate
Update-Database
Source:
https://dev.to/azure/using-entity-framework-with-azure-functions-50aa#adding-an-entity-framework-migration
It won't work for ConnectionStrings.DefaultConnection though. It will give the following error:
The property 'DefaultConnection' cannot be found on this object.
Verify that the property exists and can be set.
from everything I ready here I basically ended up with CreateDbContext(string[] args) the args will get populated with anything after you initial CMD
The -- token directs dotnet ef to treat everything that follows as an
argument and not try to parse them as options. Any extra arguments not
used by dotnet ef are forwarded to the app.
more here
will result with args being populated with 4 args
this works as the -- passes the all else back to dotnet to set the args
As an example, when adding IDesignTimeDbContextFactory<YourContext> interface to your Context adds the following method and this is where I am able to pass in the vars
public DataBaseContext CreateDbContext(string[] args)
{
Debugger.Launch();
Console.WriteLine($#"Args:");
foreach (var arg in args)
{
Console.WriteLine($"Arg: {arg}");
}
}
this will write out the 4 vars passed in -test something -testagain again
I post this answer to help expand on other answers here and see the full context
Hope this answer will help people who are trying to resolve this using .net cli.
Custom arguments can be passed to IDesignTimeDbContextFactory<T> using -- token as mentioned earlier, but I found missing actual example showing that basically there is no rule how you define them, e.g. can be -- -DbUser Admin, -- DbUser=Admin. Just make sure there is a space separator following --. The rest is forwarded to design time context factory automatically.
With all this in mind the sample usage may look like:
dotnet ef database update -c YourDbContext -- -DbUser Admin -Password Secret
dotnet ef database update -c YourDbContext -- DbUser=Admin Password=Secret
Latter seems makes more sense as parameters arrive in single dimension array, so have to map param name with param value manually if you choose the first example. Also second one looks more clear to me as extra dashes make it more confusing.
More information here: learn.microsoft.com
I had a similar issue recently trying to get an Asp.Net Core app to read the connection string. It turns out that you don't need the IDesignTimeDbContextFactory. Instead, just make sure that your context has a paramerless constructor, and use something like this in the startup:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
This should resolve to whichever connection string you have configured. If you did want to use two separate connections at the same time (which I realise you didn't want), you could do this by registering multiple DbContexts at this point; for example:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("OtherConnection")));

What's the purpose of persisting entities?

I have read the documentation many times at http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html but I still don't get it.
What's the purpose of persiting entities?
I have the following code
$em = $this->getDoctrine()->getManager();
$user = $this->container->get('security.context')->getToken()->getUser();
if ($user) {
$user->enabled(1);
$em->flush();
}
It works very well.
Why should I add the
$em->persist($user);
before the flush ?
persisting an entity just means that the entity manager can manage the entity. otherwise it doesn't know about it.
EDIT: if you are working with an entity that has been pulled from the entity manager to begin with (in your case, $user), then persisting is not required because the entity manager already "knows" about it. So persisting is only required when creating a NEW instance.
An entity can be made persistent by passing it to the EntityManager#persist($entity) method. By applying the persist operation on some entity, that entity becomes MANAGED, which means that its persistence is from now on managed by an EntityManager. As a result the persistent state of such an entity will subsequently be properly synchronized with the database when EntityManager#flush() is invoked.
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/working-with-objects.html#persisting-entities

How can I access a service outside of a controller with Symfony2?

I'm building a site that relies quite heavily on a third party API so I thought it would make sense to package up the API wrapper as a service, however I'm starting to find instances where it would be useful to have access to it outside of a controller such as in an entity repository.
Also related to that is it would be useful to be able to get access to config values outside of a controller (again such as in an entity repository).
Can anyone tell me if this is possible and if not is there a suggested approach to doing this kind of thing?
thanks for any help
The Symfony distribution relies heavily on dependency injection. This means that usually, dependencies are injected directly into your object via the constructor, the setters or via other means (like reflection over properties). Your API wrapper service is then a dependency for other objects of your application.
That being said, it would be rather difficult to inject this service in an entity repository's constructor because it already requires some other parameters and I think it would not be possible to inject them because of the way we request the repository for an entity.
What you could do is to create another service which will be responsible of doing the work you were about to do in the entity repository. This way, you will be able to inject the entity manager, which will be used to retrieve the entity repository, you custom service and also another service holding your configuration values (There are other ways to share configuration values).
In my use case, I use a Facebook helper service that wraps Facebook API calls. This service is then injected where I need it. My entity repository is only responsible of doing database calls so it receives only the arguments it needs and not the whole dependency. Thus, it will not receive the helper but rather only the arguments needed to do a request, for example, a Facebook user id. In my opinion, this is the way to do it since I think the entity repository should not have dependencies on such helper objects.
Here a small example using YAML as the configuration:
# app/config/config.yml
services:
yourapp.configuration_container:
class: Application/AcmeBundle/Common/ConfigurationContainer
# You could inject configurations here
yourapp.api_wrapper:
class: Application/AcmeBundle/Service/ApiWrapperService
# Inject other arguments if needed and update constructor in consequence
yourapp.data_access:
class: Application/AcmeBundle/Data/Access/DatabaseAccessService
arguments:
entityManager: "#doctrine.orm.entity_manager"
apiWrapperService: "#yourapp.api_wrapper"
configuration: "#yourapp.configuration_container"
# Application/AcmeBundle/Common/ConfigurationContainer.php
public ConfigurationContainer
{
public function __construct()
{
// Initialize your configuration values or inject them in the constructor
}
}
# Application/AcmeBundle/Service/ApiWrapperService.php
public ApiWrapperService
{
public function __construct()
{
// Do some stuff
}
}
# Application/AcmeBundle/Data/Access/DatabaseAccessService.php
public DatabaseAccessService
{
public function __construct(EntityManager $entityManager, ApiWrapperService $apiWrapperService, ConfigurationContainer $configuration)
{
...
}
}
The at sign (#) in the config.yml file means that Symfony should inject another service ,having the id defined after the at sign, and not a simple string. For the configuration values, as I said previously, there is other means to achieve the same goal like using parameters or a bundle extension. With a bundle extension, you could define the configuration values directly into the config.yml and your bundle would read them.
In conclusion, this should give you the general idea of injecting services. Here a small list of documentation on the subject. Alot of links use the XML service definition instead of the YAML definition but you should be able to understand them quite easily.
Symfony Official DI
Fabien Potencier's articles on DI
Richard Miller's articles on DI (Check in his blog for the other DI articles)
Take note that the configuration I'm giving is working for Beta1 of Symfony2. I didn't update yet to Beta2 so there could be some stuff not working as they are in the Beta2 version.
I hope this will help you defining a final solution to your problem. Don't hesitate to ask other questions if you want clarifications or anything else.
Regards,
Matt
I would wrap this kind of behavior in a Symfony service(like a manager).
i would not inject any parameters or logic into the entity repositories, as they should mainly be used to fetch data using object manager queries.
I would put the logic in the services and if the service , require a database access it will call the entity repository to fetch data.

Resources