Shopware 6 Create a default entity in migration file - shopware

How can i store a default record using the migration file?
In symfony/doctrine there are a method like postUp() which is missing in shopware.
So how can i do this? The shopware doc provides a tutorial Creating data with a given ID, but i cannot initialize the needed repository.
So how can i access the entityRepository inside the migration class? Or is there another way to create a default record?

The recommended way is to just create the table in the migration file and then upsert the default records in the activate and update methods of the Plugin class. See answer by #dneustadt
Please read the comments to this answer for further information.
You can't use an EntityRepository inside a migration file. You can only use raw SQL queries by using the $connection variable which is passed to the update method.
Example:
<?php declare(strict_types=1);
namespace Swag\BasicExample\Migration;
use Doctrine\DBAL\Connection;
use Shopware\Core\Framework\Migration\MigrationStep;
use Shopware\Core\Framework\Uuid\Uuid;
class Migration1611740369ExampleDescription extends MigrationStep
{
public function getCreationTimestamp(): int
{
return 1611740369;
}
public function update(Connection $connection): void
{
// create table
$createTableSql = <<<SQL
CREATE TABLE IF NOT EXISTS `swag_basic_example_general_settings` (
`id` INT NOT NULL,
`example_setting` VARCHAR(255) NOT NULL,
`created_at` DATETIME(3) NOT NULL,
`updated_at` DATETIME(3),
PRIMARY KEY (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
SQL;
$connection->executeStatement($createTableSql);
// insert default setting
$insertDefaultSettingSql = <<<SQL
INSERT INTO `swag_basic_example_general_settings`
(`id`, `example_setting`, `created_at`)
VALUES (:id, :example_setting, NOW())
SQL;
$connection->executeStatement(
$insertDefaultSettingSql,
[
'id' => Uuid::randomBytes(),
'example_setting' => 'example_value',
]
);
}
public function updateDestructive(Connection $connection): void
{
}
}
Note: When using Connection instead of an entity repository you need to use Uuid::randomBytes() instead of Uuid::randomHex(). If you want to use an existing ID instead of a generated one you can use Uuid::fromHexToBytes('0fa91ce3e96a4bc2be4bd9ce752c3425')

Inside the Plugin extension you can access the DI container and get any public services including the repositories:
class MyPlugin extends Plugin
{
public function activate(ActivateContext $context)
{
$productRepository = $this->container->get('product.repository');
}
}

Related

Association is in database, but can't be retrieved via DAL. Will retrieve empty array of associations

I am following the advanced developer tutorial (https://docs.shopware.com/en/shopware-platform-dev-en/how-to/indepth-guide-bundle).
Currently I'm at step 7, and according to the tutorial what I've made so far should work.
But it doesn't.
In the database it shows the association, but I can't retrieve them from the repository.
You have to add the association to the Criteria.
$criteria->addAssociation("name_of_association")
Without it, the associations come as null.
Okay, turns out I switched up two parameters by accident. When I set them correctly it worked as it should.
<?php declare(strict_types=1);
namespace Swag\BundleExample\Core\Content\Product;
use Shopware\Core\Content\Product\ProductDefinition;
use Shopware\Core\Framework\DataAbstractionLayer\EntityExtension;
use Shopware\Core\Framework\DataAbstractionLayer\Field\Flag\Inherited;
use Shopware\Core\Framework\DataAbstractionLayer\Field\ManyToManyAssociationField;
use Shopware\Core\Framework\DataAbstractionLayer\FieldCollection;
use Swag\BundleExample\Core\Content\Bundle\Aggregate\BundleProduct\BundleProductDefinition;
use Swag\BundleExample\Core\Content\Bundle\BundleDefinition;
class ProductExtension extends EntityExtension
{
public function extendFields(FieldCollection $collection): void
{
$collection->add(
(new ManyToManyAssociationField(
'bundles',
BundleDefinition::class,
BundleProductDefinition::class,
'product_id',
'bundle_id'
))->addFlags(new Inherited())
);
}
public function getDefinitionClass(): string
{
return ProductDefinition::class;
}
}
I'm talking about the 'product_id' and 'bundle_id'. In my case I had the 'product_id' as the last parameter.

How to hardcode the entity varchar value?

The requirement is to store the hardcoded value for varchar which is in an entity file(.eti). I tried adding to the default option but it is not reflecting.
Default option works well with boolean values (true/false), typelists (you can choose a default typecode), monetary amounts too, but it looks like it is not allowed to specify a default varchar.
Therefore the easiest way would be to create a preupdate rule which inserts that default value every time when you create a new record in the database.
Preupdate rule example:
#gw.rules.RuleName("YourEntityAssignDefaultValue")
internal class YourEntityAssignDefaultValueRule {
static function doCondition(yourEntity : entity.YourEntity) : boolean {
return yourEntity.New
}
static function doAction(yourEntity : entity.YourEntity, actions : gw.rules.Action) {
yourEntity.yourColumn = "defaultValue"
}
}
you can achieve through getter and setter properties in an appropriate enhancement class.
public property get PolicyNumber(): String {
return this.PolicyNumber }
and somewhere class you must be assigned the value to the PolicyNumber field then it will reflect.

Breeze & EFContextProvider - How to properly return $type when using expand()?

I am using Breeze with much success in my SPA, but seem to be stuck when trying to return parent->child data in a single query by using expand().
When doing a single table query, the $type in the JSON return is correct:
$type: MySPA.Models.Challenge, MySPA
However if I use expand() in my query I get the relational data, but the $type is this:
System.Collections.Generic.Dictionary 2[[System.String, mscorlib],[System.Object, mscorlib]]
Because of the $type is not the proper table + namespace, the client side code can't tell that this is an entity and exposes it as JSON and not a Breeze object (with observables, entityAspect, etc.).
At first I was using my own ContextProvider so that I could override the Before/After saving methods. When I had these problems, I reverted back to the stock EFContextProvider<>.
I am using EF5 in a database first mode.
Here's my controller code:
[BreezeController]
public class DataController : ApiController
{
// readonly ModelProvider _contextProvider = new ModelProvider();
readonly EFContextProvider<TestEntities> _contextProvider = new EFContextProvider<TestEntities>();
[HttpGet]
public string Metadata()
{
return _contextProvider.Metadata();
}
[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
[HttpGet]
public IQueryable<Challenge> Challenges()
{
return _contextProvider.Context.Challenges;
}
[HttpPost]
public SaveResult SaveChanges(JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
public IQueryable<ChallengeNote> ChallengeNotes()
{
return _contextProvider.Context.ChallengeNotes;
}
}
Here's my BreezeWebApiConfig.cs
public static void RegisterBreezePreStart()
{
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "BreezeApi",
routeTemplate: "breeze/{controller}/{action}"
);
}
Is there a configuration setting that I am missing?
Did you try "expanding" on server side? Is it needed to do expand on client side? I tried to do expand before but failed for me as well, did some research and decided I'd rather place it on server:
[HttpGet]
public IQueryable<Challenge> ChallengesWithNotes()
{
return _contextProvider.Context.Challenges.Include("ChallengeNotes");
}
This should be parsed as expected. On client side you would query for "ChallengeNotes" instead of "Challenges" and you wouldn't need to write expand part.
I strongly suspect that the problem is due to your use of the [Queryable] attribute.
You must use the [BreezeQueryable] attribute instead!
See the documentation on limiting queries.
We are aware that Web API's QueryableAttribute has been deprecated in favor of EnableQueryAttribute in Web API v.1.5. Please stick with BreezeQueryable until we've had a chance to write a corresponding derived attribute for EnableQuery. Check with the documentation for the status of this development.

Default values for properties in Azure Table Storage

I am using Azure table storage and have questions about how nulls and default values for entities work.
Specifically, I have a class that extends TableServiceEntity. The default constructor for this class is setting default values for various properties like so:
public class MyEntity: TableServiceEntry
{
public MyEntity() : MyEntity("invalid", "invalid") {}
public MyEntity(string f1, string f2)
{
Field1 = f1;
Field2 = f2;
}
public string Field1 { get; set; }
public string Field2 { get; set; }
}
I tested this class locally (on the emulator) by constructing the following entity:
MyEntity e = new MyEntity("hello", null);
I uploaded the entity and then retrieved it locally and the two fields were set to "hello" and null, respectively, as expected.
However, when I uploaded the same entity to the Azure cloud, what I received back was "hello" and "invalid", respectively, for the two properties.
My code that saves the entity is below:
public class MyTable : TableServiceContext
{
...
public void AddEntry(MyEntity e)
{
this.AddObject("MyTable", e);
this.SaveChangesWithRetries(SaveChangesOptions.ReplaceOnUpdate);
}
}
I was able to fix this by making the default constructor take no arguments, but now I feel like I have a fundamental misunderstanding of how table storage works. Is it true that when you specify defaults for properties of a TableServiceEntry, those become the defaults for each row in the table in the cloud but not in the emulator (i.e. cloud vs. SQL Express)? If so, why can't I override those defaults with null in the cloud? Is there any documentation that explains how default constructors and nulls work in Azure table storage?
Yes, there is a difference between how table storage behaves in the emulator and in the cloud. The emulator implemented in SQL server, returns all columns defined for a table, even if not defined for a row, irrespective of the columns value (null / non-null). In the cloud, a property set to null is neither stored nor returned in the REST call.
A quick fix would be to check for null in property set, and only mutate the property if the value passed in, is not null.
Devstorage and real storage behave differently in some cases, but I've never seen them handle NULL values differently. And I've certainly never seen it change a value from NULL to "invalid", as you seem to be implying. Are you sure you didn't accidentally upload the wrong values to the cloud? You may want to try again, and use Fiddler to look at the actual request and response values.

Kohana 3.2 Using Auth Module on multiple databases

I'm using the Module Auth with ORM driver and native sessions.
The database config 'default' and 'customer_1' exists in application/config/database.php.
Before login i change the default database config with:
Kohana::$config->load('database')->default = Kohana::$config->load('database')->get('customer_1');
This does work before Module Auth login!
After setting the default database config:
if (Auth::instance()->login($_POST['username'], $_POST['password']) === TRUE) { Request::current()->redirect(); }
This results in the following error:
Table 'default_database.users' doesn't exist [ SHOW FULL COLUMNS FROM `users` ]
For some reason it use the initial default database config.
My Question: How do i set the default database for Module Auth ?
Let's follow this through a bit.
You're actually using ORM/Auth and not just Auth. ORM in ORM/Auth is configured to use the default database if one isn't specified. It lets you override this option by overloading $_db_group in the ORM.php file.
Let's use Kohana's cascading filesystem to overwrite that. Make a new file: classes/auth.php . Insert this code:
<?php
class ORM extends Kohana_ORM {
$_db_group = Kohana::$config->load('database')->get('customer_1');
}
All set.
If you want Auth module to use different database then other models, you should use $_db_group as suggested by Gaurav Patel. However you should override only Auth ORM models (user, role and user_token), not ORM class:
APPATH/classes/model/user.php:
class Model_User extends Model_Auth_User
{
protected $_db_group = 'customer_1';
}
APPATH/classes/model/role.php:
class Model_Role extends Model_Auth_Role
{
protected $_db_group = 'customer_1';
}
APPATH/classes/model/user/token.php:
class Model_User_Token extends Model_Auth_User_Token
{
protected $_db_group = 'customer_1';
}

Resources