Recommended way to add a new entity to sylius core bundle? - entity-framework

Currently using:
base install of sylius-standard, for an ecommerce website.
Here is what I would like to accomplish :
Basically we know there is an User Entity which resides in
vendor/sylius/sylius/src/Sylius/Component/Core/Model/User.php
This Entity is actually extending the FOS\UserBundle\Model\User and it also has a linked UserInterface which is implementing FOS\UserBundle\Model\UserInterface.
My goal is to create another entity UserData which can be linked via the Doctrine OneToMany field relation to the above sylius User Entity which is basically the table sylius_user.
So effectively User Entity linked to UserData Entity via oneToMany ( For each user there can be multiple UserData entity instances.)
Sylius documentation (http://sylius.readthedocs.org) does not have anything related to adding an entity - If I am mistaken, please guide me to the correct links.
I have read overriding models at - > sylius models override also have gone through another sort of related question on stackoverflow How to create a new model with Sylius via SyliusResourceBundle but it is not clear as he is using the SyliusResourceBundle while I want to extend the SyliusCoreBundle (not sure if we can extend it in the first place or not)
How do I achieve this in the sylius framework ?

I solved the problems by reading segregated posts, so putting in here the solution for the community (please let me know if this is not the recommended way and the correct way if not):
My goal was to create a oneToMany doctrine relationship between
Sylius\Component\Core\Model\User
And
Acme\Bundle\Entity\UserData
which I had created. Basically for every user there could be multiple userdata instances pointing back to that user.
To do this, at first I followed the docs at sylius documentation
to override the sylius user class which is infact an extended class of fosuserbundle.
At some stage I kept getting the error [Doctrine\DBAL\Schema\SchemaException]
The table with name 'astrohealing_dev.sylius_user' already exists.
Based on Winzou's suggestion : To fix this error I changed the user class locations in sylius.yml (Sylius/Bundle/CoreBundle/Resources/config/app/sylius.yml) which is part of the core bundle and declared my own class as the user model like this :
This resolved the sylius_user table exists error
But then I got another error :
[Symfony\Component\Config\Definition\Exception\ForbiddenOverwriteException]
Configuration path "sylius_core.driver" cannot be overwritten. You have to define all options for this path, and any of its sub
-paths in one configuration section.
Next I commented out drive : doctrine/orm line for the relevant entry in
app/config/config.yml
sylius_core:
# driver: doctrine/orm
classes:
user:
model: Acme\Bundle\Entity\User
sylius_resource:
resources:
sylius.userdata:
driver: doctrine/orm
classes:
model: Acme\Bundle\Entity\UserData
The above few lines also show :
The override for sylius_core with my new User class
The new resource - which is bascially just the userdata class
Now for the Doctrine Mapping (User.orm.xml):
<?xml version="1.0" encoding="UTF-8"?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\User" table="sylius_user">
<one-to-many
field="userdata"
target-entity="Acme\Bundle\Entity\UserData"
mapped-by="userid" />
</entity>
</doctrine-mapping>
And the mapping for UserData (UserData.orm.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="Acme\Bundle\Entity\UserData" table="sylius_userdata">
<id name="id" type="integer">
<generator strategy="AUTO" />
</id>
<many-to-one
field="userid"
target-entity="Acme\Bundle\Entity\User"
inversed-by="userdata"
join-column="userid">
<join-column name="user_id" referenced-column-name="id" nullable="false" />
</many-to-one>
<field name="name" type="string" length="150" />
<field name="datetime" type="datetime" />
</entity>
</doctrine-mapping>

Related

Disable form fields in Sulu's admin frontend after the entity has been persisted

I'd like to connect the key of a category in the Sulu CMS with specific business logic. But therefore the key must not be changeable in the admin area. Is there a way to disable a form field once the entity is persisted?
It's possible to override any form configuration by creating a file with the same name and the same <key> in the config/forms/ directory of your project. In case of the category details form, create a config/forms/category_details.xml file with <key>category_details</key> containing only the properties you want to override, in your case the <property name="key">. You can omit the other properties, because all the form configurations with the same key will be merged together. Then you can use a disabledCondition to configure when this property should be disabled and when it should be possible to edit, in your case something like disaledCondition="!!id", because then the property is only editable, as long as it doesn't have an id, which is only the case when you create a new category. You probably also want to add the mandatory="true" attribute, if your application depends on the key of a category. So the whole file should look like this:
<?xml version="1.0" ?>
<form xmlns="http://schemas.sulu.io/template/template"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://schemas.sulu.io/template/template http://schemas.sulu.io/template/form-1.0.xsd"
>
<key>category_details</key>
<properties>
<property name="key" type="text_line" mandatory="true" disabledCondition="!!id">
<meta>
<title>sulu_admin.key</title>
</meta>
</property>
</properties>
</form>

Set Allowed Countries on Store View

In Magento 1.4, I was able to set allowed countries on the Store View Level, therefore I could have a Website with one Store und multiple Store Views for each of my countries:
Now in Magento 2, I can only set the Allowed Countries on the Website and not on the Store View, the Store View setting looks as follows:
Why do I want to change that? I need to be able to set a different store contact address for each of these Store Views, because I e.g. have an Argentinien und a Bulgarian Store View, so I want to set the different addresses but use the same Website/Store.
Unfortunately, I'm also not able to change the Store Contact Address per Store View anymore, this also only works on Website Level.
Am I missing something? Was there a logical change from 1.X to 2.X about the Store Views?
I don't know why the allowed country option was removed from settings in store view. But looking in the code shows that the information is used if present. So you can just enter the data into core_config_data (scope: stores, scope_id: your_store_id, value: AT,AB,AC...
the correct answer that respects Magento 2 standardization is overloading the system.xml of the magento/Backend/etc/adminhtml.
you should try:
Vendor/ModuleName/etc/adminhtml/system.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="general">
<group id="country" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Country Options</label>
<field id="allow" translate="label" type="multiselect" sortOrder="2" showInDefault="1" showInWebsite="1" showInStore="1" canRestore="1">
<label>Allow Countries</label>
<source_model>Magento\Directory\Model\Config\Source\Country</source_model>
<can_be_empty>1</can_be_empty>
</field>
</group>
</section>
</system>
</config>
Remember to add overridden module - Magento_Backend
Vendor/ModuleName/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_YourModule" setup_version="1.0.0">
<sequence>
<module name="Magento_Backend"/>
</sequence>
</module>
</config>

codefluent custom stored procedure

I have a custom stored procedure with in parameters that return fields of different tables how I can map this custom stored to an entity? I only want to use like a read only values for a report I don't want to save or something like that I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null
Solution 1: Using a view
A view allows to aggregate data from different entities.
<Article>
<Id />
<Name />
<Lines typeName="LineCollection" />
<cf:method name="LoadArticlesByCommand" body="load(string commandName) from ArticleByCommand where CommandName = #commandName" />
<cf:view name="ArticleByCommand" autoLightweight="true">
<ArticleName expression="Name"/>
<ArticleQty expression="Lines.Quantity" />
<CommandName expression="Lines.Command.Name" />
</cf:view>
</Article>
<Command>
<Id />
<Name />
<Lines typeName="LineCollection" />
</Command>
<Line setType="List">
<Article typeName="Article" key="true" />
<Command typeName="Command" key="true" />
<Quantity typeName="int" />
</Line>
http://blog.codefluententities.com/2014/04/22/views-auto-lightweight-and-the-modeler/
https://www.softfluent.com/documentation/Views_PersistentViews.html
Solution 2: Using a lightweight entity
Instead of creating a view, you can can create a lightweight entity that contains only the properties used by the stored procedure.
<cf:entity name="Person" lightweight="true">
<cf:property name="FirstName" typeName="string" />
<cf:property name="lastName" typeName="string" />
<cf:method name="ComputeBalance"
body="load () raw"
rawBody="SELECT 'John' AS FirstName, 'Doe' AS LastName" />
</cf:entity>
Solution 3: Custom mapping
For more specific values or types, a custom method can be provided to map the database values to .NET types. This custom method will be called with a DataReader as parameter, meaning that a developer could do whatever he wants.
<cf:entity name="Sample">
<cf:method name="LoadPair" body="raw" rawBody="SELECT 1234,5678"
returnTypeName="CodeFluent.Runtime.Utilities.Pair<System.Int32,System.Int32>"
cfom:methodName="On{0}" />
<cf:snippet>
private static CodeFluent.Runtime.Utilities.Pair<int,int> OnLoadPair(System.Data.IDataReader reader)
{
return new Pair<int, int>(reader.GetInt32(0), reader.GetInt32(1));
}
</cf:snippet>
</cf:entity>
You can also use OnAfterReadRecord or OnBeforeReadRecord rules
If it is not essential that you map the results of the custom stored procedure to an entity than another option is to use the built in support for DataSets.
http://blog.codefluententities.com/2011/06/22/dataset-support-in-codefluent-entities/
<cf:method name="LoadAllCities" body="raw" returnTypeName="System.Data.DataSet">
SELECT $Address::City$ FROM $Address$
</cf:method>
.
DataSet ds = Address.LoadAllCities();
foreach (DataTable table in ds.Tables)
{
foreach (DataRow row in table.Rows)
{
Console.WriteLine("City: " + row[0]);
}
}
Upon re-reading you're question I am providing another answer.
In response to the part where you said "I try to add the extra fields to the most similar entity but when I execute the method in code the extra fields are null". The following steps should be able to solve that problem.
Execute one of the automatically created stored procedure in SQL Management Studio.
Execute the stored procedure you manually created.
Verify that the fieldnames returned by both stored procedures match.
I think the above will solve your immediate problem but I still don't like the solution. The reason is that you said you picked the most similar entity. I think that is going to cause problems in the future especially if the stored procedure is not being mapped to all of the entities properties.
I would recommend either lightweight entity, view or DataSet.

How to create a repository in ATG?

i would like to know how to create a new repository in ATG. like what all steps are needed to be included? Do i need to create a properties file?
In order to create a new repository, You need to follow these steps if you want to create a repository that uses sql database in as the datastore.
Create a properties file
Create tables you want to map to the repository
Create a definition XML files to map repository item-descriptors and their properties to the respective tables.
MyRepository.properties
$class=atg.adapter.gsa.GSARepository
repositoryName=MyRepository
definitionFiles=atg/test/repositories/MyRepository.xml
XMLToolsFactory=/atg/dynamo/service/xml/XMLToolsFactory
transactionManager=/atg/dynamo/transaction/TransactionManager
idGenerator=/atg/dynamo/service/IdGenerator
dataSource=/atg/dynamo/service/jdbc/JTDataSource
lockManager=/atg/dynamo/service/ClientLockManager
The above properties files ensures that a new repository component is created and MyRepository.xml will be marked as its definition file.
The content of the MyRepository.xml file should be something like below...
MyRepository.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE gsa-template SYSTEM "http://www.atg.com/dtds/gsa/gsa_1.0.dtd">
<gsa-template>
<header>
<name>New Repository creation</name>
<author>Jyothi Prasad Buddha</author>
</header>
<item-descriptor name="myRepo" cache-mode="simple">
<table name="my_repo" type="primary" id-column-names="id">
<!-- properties that may (or may not) be used as primary keys -->
<property name="name" data-types="String" />
<property name="age" data-types="int" />
</table>
</item-descriptor>
</gsa-template>
However you will have to create the the necessary tables before you start atg instance. The above xml files refers to a table named my_repo which has comlumns name and age.
Yes you need to create properties file's for more details you can read RepositoryGuide.pdf from Oracle.
for more detail you can read below blog
http://immuraliraj.blogspot.in/2011/12/atg-repository-basic-concepts.html
Just type
"ATG Creating a repository" on Google
and you would get lot of relevant results on the first page itself. Also be specific which version of ATG are you using, and what you have already done/found in your research. (just a suggestion, so you get good responses).
Yes, you need a properties file when creating a custom repository component. There are a lot of blogs that answer your question with detailed steps.
Check this for example

Entity Framework designer custom property does not appear in DDL generation

I have added a custom property to the Properties dialog of the Entity Framework 5 designer through
http://msdn.microsoft.com/en-us/library/microsoft.data.entity.design.extensibility.ientitydesignerextendedproperty(v=vs.103).aspx
This works well, the property appears in the Properties dialog and it is saved in the EDMX file.
Now I'd like to use that property in the DDL generation process. I have edited the T4 template file SSDLToSQL10.tt (found at C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen).
However, the custom property doesn't seem to appear anywhere in the metadata tree. The website (in German)
http://www.databinding.net/en/blog/post/2010/12/13/entity-framework-4-erweiterte-eigenschaften-in-einer-t4-vorlage-verwenden.html
tells me that the extended property should appear in the EntityType.MetadataProperties collection, but this collection contains only the following members:
KeyMembers Members Name NamespaceName Abstract BaseType DataSpace MetadataProperties
None of those is my custom property.
Am I missing something? How can I access the IEntityDesignerExtendedProperty's value in the T4 code generation template?
EDIT: Here is the EDMX part with the custom property:
<edmx:ConceptualModels>
<Schema ...>
....
<EntityType Name="Entity1">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Guid" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="None" />
<Property Type="String" Name="Name" Nullable="false" />
<a:MyNewProperty xmlns:a="http://schemas.tempuri.com/MyNewProperty">True</a:MyNewProperty>
</EntityType>
I guess I have to map that custom property from CSDL to SSDL somehow.
You added the property to the CSDL (conceptual layer) while the DDL is created using the SSDL (store layer). You should be able to access the conceptual model in the SSDLToSQL10.tt but I don't think it is really what you are after. In general your property is not something the EF runtime can really use - I believe it will be just treated as an extension and ignored. If you want to add a property that is supposed to be used by the EF runtime the property must be declared in the CSDL (conceptual layer) and SSDL (store layer) and mapped correctly in the MSL (mapping layer) - with the latter being probably the most difficult.
Unless I am missing what you are trying to achieve you are probably using a wrong extension point. The IEntityDesignerExtendedProperty allows defining custom properties that shows in the property and the model browser windows in the designer but are ignored at runtime. For me it looks like you would like to add a property automatically to your model. For that I would try using the IModelTransformationExtension where you should be given the entire edmx which you will be able to modify at will (i.e. CSDL, SSDL, MSL and add elements (properties) in correct EF xml namespaces). I would try using OnBeforeModelSaved since I believe the model will be saved automatically before trying to generate the database.
I was able to achieve what I want using the edmx:CopyToSSDL=true attribute:
<edmx:ConceptualModels>
<Schema ...>
....
<EntityType Name="Entity1">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Guid" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="None" />
<Property Type="String" Name="Name" Nullable="false" />
<a:MyNewProperty edmx:CopyToSSDL="true"
xmlns:a="http://schemas.tempuri.com/MyNewProperty">
True
</a:MyNewProperty>
</EntityType>
This way, the transformator that generates the SSDL from the CSDL copies the annotation over to the SSDL, so I'm able to access it in the T4 template that generates the DDL SQL file.
If someone is going to use this in Entity Framework 5, please not that there is a bug (http://entityframework.codeplex.com/workitem/702), and you can workaround by using the old EDMX XML namespace:
<a:MyNewProperty edmxv2:CopyToSSDL="true"
xmlns:a="http://schemas.tempuri.com/MyNewProperty"
xmlns:edmxv2="http://schemas.microsoft.com/ado/2008/10/edmx">
True
</a:MyNewProperty>