I need to implement versioning for some entities.
I have an entity "Map" which has a OneToMany association with "Spot" Entities.
The "Map" and also "Spot" should be versionable.
It should be possibler to show older versions of a "Map" with all the asociated "Spots" on it.
So on an old version the "Map" itself could have another backgound-image, but also the position or number of associated "Spots" can differ.
I like this approach of the AuditLog (at the end of the page):
http://www.doctrine-project.org/blog/doctrine2-versionable.html
[php]
class AuditListener implements EventSubscriber
{
public function getSubscribedEvents()
{
return array(Events::onFlush);
}
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$changeDate = new DateTime("now");
$class = $em->getClassMetadata('DoctrineExtensions\Auditable\AuditEntry');
foreach ($uow->getScheduledEntityUpdates() AS $entity) {
if ($entity instanceof Auditable) {
$changeSet = $uow->getEntityChangeSet($entity);
foreach ($changeSet AS $field => $vals) {
list($oldValue, $newValue) = $vals;
$audit = new AuditEntry(
$entity->getResourceName(),
$entity->getId(),
$oldValue,
$newValue,
$changeDate
);
$em->persist($audit);
$em->getUnitOfWork()
->computeChangeSet($class, $audit);
}
}
}
}
}
I wonder how to handle association of the versionable entity.
For Example:
When a "Map" changes, a new Map Version is saved, but what about the Association to the Spots?
When a "Spot" changes, what about its parent "Map".
When buliding the new Version of "Map":
- how can I figure out that it has Association?
- how can I figure out that a Association is also Versionable
- how do I handle the Associatons
Even if an association is not versionable, if I change an associated non-versionable entity I also change the old versions of "Map" because they are also still associated.
Does anyone has experience or ideas how to manage that with doctrine 2.1?
Related
We are using Spark job with emr-dynamodb-connector to load the data from S3 files into Dyanamodb.
https://github.com/awslabs/emr-dynamodb-connector
But if document is already present in dynamodb, my code is replacing it.
Is there a way to avoid updating existing records (based on id) if they are present in Dynamodb. If id is present in dynamodb, i simply don't want to update it, just skip that id and write rest of records. Code i am using is
JobConf ddbConf = new JobConf(spark.sparkContext().hadoopConfiguration());
ddbConf.set("dynamodb.output.tableName", tableName);
ddbConf.set("dynamodb.throughput.write.percent", "50");
ddbConf.set("mapred.input.format.class", "org.apache.hadoop.dynamodb.read.DynamoDBInputFormat");
ddbConf.set("mapred.output.format.class", "org.apache.hadoop.dynamodb.write.DynamoDBOutputFormat");
JavaPairRDD<Text, DynamoDBItemWritable> ddbInsertFormattedRDD = finalDatasetToBeSaved.toJavaRDD().mapToPair(new PairFunction<Row, Text, DynamoDBItemWritable>() {
#Override
public Tuple2<Text, DynamoDBItemWritable> call(Row row) throws Exception {
Map<String, AttributeValue> ddbMap = new HashMap<String, AttributeValue>();
for (int i = 0 ; i <= schemaDdb.length - 1; i++) {
Object value = row.get(i);
if (value != null) {
AttributeValue att = new AttributeValue();
if(schemaDdb[i]._2.toString().equalsIgnoreCase("IntegerType")){
att.setN(value.toString());
}else{
att.setS(value.toString());
}
ddbMap.put((String)schemaDdb[i]._1, att);
}
}
DynamoDBItemWritable item = new DynamoDBItemWritable();
item.setItem(ddbMap);
return new Tuple2<Text, DynamoDBItemWritable>(new Text(""), item);
}
});
ddbInsertFormattedRDD.saveAsHadoopDataset(ddbConf);
By saying Is there a way to avoid updating existing records (based on id) if they are already present, Do you want to add another document instead of replacing/updating it?
If yes, I am afraid it wont be possible with primary key, since that should be unique and distinguishes it from other. You need to make a key non-primary in order to do this.
If you want to ignore the insertion (if item exists), you can use condition-expression attribute_not_exists(your-key) as defined in the documentation
Under Symfony4.2, I have a Translate entity (id, gb_name, fr_name) and LocationCountry entity (id, ISO3166-2 name: GB,FR, DE…, translate_id)
I define a CSV file with 255 countries ("GB", "Great Britain", "Angleterre"…) and I want to push it in Translate and LocationCountry entities tables with DataFixture.
I read carefully https://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html#sharing-objects-between-fixtures
and
php create fixtures with automatic relations
src/DataFixtures/TranslateFixtures.php:
if ($csv_handle) {
while ($item = fgetcsv($csv_handle, $csv_max_line_length, $csv_delimiter, $csv_enclosure)) {
$obj = new Translate();
$obj->setGbValue($item[1]);
$obj->setFrValue($item[2]);
$this->addReference('country'.$item[0], $obj);
$manager->persist($obj);
}
fclose($csv_handle);
}
$manager->flush();
I am not sure addReference should be before flush() ?
src/DataFixtures/LocationCountryFixtures.php:
if ($csv_handle) {
while ($item = fgetcsv($csv_handle, $csv_max_line_length, $csv_delimiter, $csv_enclosure)) {
$translate_country = $this->getReference('country'.$item[0]);
$obj = new LocationCountry();
$obj->setIso3166Name($item[0]);
$obj->setTranslate($translate_country);
$manager->persist($obj);
}
fclose($csv_handle);
}
$manager->flush();
}
public function getDependencies() {
return array(
Translate::class,
);
}
If I remove addReference Translate entity is well filled.
But with the code above, it returns error:
In SymfonyFixturesLoader.php line 76:
The "App\Entity\Translate" fixture class is trying to be loaded, but is not available. Make sure this class is defined as a service and tagged with "doctrine.fixture.orm".
I think to have the right Use:
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use App\Entity\LocationCountry;
use App\Entity\Translate;
Thank for your help
Doctrine loads the fixture files in alphabetical order; that's why you get an error. You can consider using function getOrder in your fixtures in order to set which one will be load first.
EDIT :
You get an error because you don't provide the right class in your getDependencies method :
public function getDependencies() {
return array(
TranslateFixtures::class,
);
}
In this case, I just remove src/DataFixtures/TranslateFixtures.php
and change src/DataFixtures/LocationCountryFixtures.php to do all job:
if ($csv_handle) {
while ($item = fgetcsv($csv_handle, $csv_max_line_length, $csv_delimiter, $csv_enclosure)) {
//$translate_country = $this->getReference('country'.$item[0]);
$country = new LocationCountry();
$translate_country = new Translate();
$translate_country->setGbValue($item[1]);
$translate_country->setFrValue($item[2]);
$country->setIso3166Name($item[0]);
$country->setTranslateCountry($translate_country);
$manager->persist($translate_country);
$manager->persist($country);
}
fclose($csv_handle);
}
$manager->flush();
I have created workflow in my sitecore project and on final state ( Approval ) I just want auto publish to a particular database.
So where should I do the changes to point to database.
Thanks
In order to perform automatic publishing, your final state should contain a workflow action, that does the job for you. You may take a look on Sample Workflow (that comes by default with Sitecore) - Approved state. It contains child item Auto Publish, that has two fields.
Type string:
Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel
sets the class that in fact does publishing. You may inherit from that class and implement your own behavior, supply extra parameters etc. I would advise you to take dotPeek or Reflector and look-up this class implementation so that you may adjust your own code.
Parameters:
deep=0
..stands for publishing child items recursively.
Update: Lets take a look on decompiled class from Sample Workflow Auto Publish action:
public class PublishAction
{
public void Process(WorkflowPipelineArgs args)
{
Item dataItem = args.DataItem;
Item innerItem = args.ProcessorItem.InnerItem;
Database[] targets = this.GetTargets(dataItem);
PublishManager.PublishItem(dataItem, targets, new Language[1]
{
dataItem.Language
}, (this.GetDeep(innerItem) ? 1 : 0) != 0, 0 != 0);
}
private bool GetDeep(Item actionItem)
{
return actionItem["deep"] == "1" || WebUtil.ParseUrlParameters(actionItem["parameters"])["deep"] == "1";
}
private Database[] GetTargets(Item item)
{
using (new SecurityDisabler())
{
Item obj = item.Database.Items["/sitecore/system/publishing targets"];
if (obj != null)
{
ArrayList arrayList = new ArrayList();
foreach (BaseItem baseItem in obj.Children)
{
string name = baseItem["Target database"];
if (name.Length > 0)
{
Database database = Factory.GetDatabase(name, false);
if (database != null)
arrayList.Add((object)database);
else
Log.Warn("Unknown database in PublishAction: " + name, (object)this);
}
}
return arrayList.ToArray(typeof(Database)) as Database[];
}
}
return new Database[0];
}
}
GetTargets() method from above default example does publishing to all targets that are specified under /sitecore/system/publishing targets path. As I mentioned above, you may create your own class with your own implementation and reference that from workflow action definition item.
You can look into Sample workflow's Auto publish action. But in general you can create a Workflow Action with type: Sitecore.Workflows.Simple.PublishAction, Sitecore.Kernel and set parameters as deep=1&related=1&targets=somedb,web&alllanguages=1
Basically, I had the same problem as here:
Symfony2 & Translatable : entity's locale is empty
Translations where saved in the ext_translations table, but where not being displayed.
After adding the proposed fix, it DID work.
Today I upgraded from 2.0 to 2.1 I managed to get pretty much everything working so far.
But now my translatables are again not being displayed properly (they ARE still being saved properly).
I think it has something to do with the changes to where and how the users locale is stored in 2.1 compared to 2.0 .. but i cannot figure this one out.
Fixed this by registering a custom listener
namespace XXX;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class LocaleListener implements EventSubscriberInterface
{
private $defaultLocale;
public function __construct($defaultLocale = 'en')
{
$this->defaultLocale = $defaultLocale;
}
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if (!$request->hasPreviousSession()) {
return;
}
if ($locale = $request->attributes->get('_locale')) {
$request->getSession()->set('_locale', $request->getLocale());
} else {
$request->setDefaultLocale($request->getSession()->get('_locale', $this->defaultLocale));
}
}
static public function getSubscribedEvents()
{
return array(
// must be registered before the default Locale listener
KernelEvents::REQUEST => array(array('onKernelRequest', 17)),
);
}
}
then changed
$request->setDefaultLocale($request->getSession()->get('_locale', $this->defaultLocale));
to
$request->setLocale($request->getSession()->get('_locale'));
and used
$this->getRequest()->getSession()->set('_locale', 'nl');
to set the locale, translations and translatables now work
hope this also helps someone else ..
Is there a way to retrieve the set-at-creations properties of an EmberJS object if you don't know all your keys in advance?
Via the inspector I see all the object properties which appear to be stored in the meta-object's values hash, but I can't seem to find any methods to get it back. For example object.getProperties() needs a key list, but I'm trying to create a generic object container that doesn't know what it will contain in advance, but is able to return information about itself.
I haven't used this in production code, so your mileage may vary, but reviewing the Ember source suggests two functions that might be useful to you, or at least worth reviewing the implementation:
Ember.keys: "Returns all of the keys defined on an object or hash. This is useful when inspecting objects for debugging. On browsers that support it, this uses the native Object.keys implementation." Object.keys documentation on MDN
Ember.inspect: "Convenience method to inspect an object. This method will attempt to convert the object into a useful string description." Source on Github
I believe the simple answer is: you don't find a list of props. At least I haven't been able to.
However I noticed that ember props appear to be prefixed __ember, which made me solve it like this:
for (f in App.model) {
if (App.model.hasOwnProperty(f) && f.indexOf('__ember') < 0) {
console.log(f);
}
};
And it seems to work. But I don't know whether it's 100% certain to not get any bad props.
EDIT: Adam's gist is provided from comments. https://gist.github.com/1817543
var getOwnProperties = function(model){
var props = {};
for(var prop in model){
if( model.hasOwnProperty(prop)
&& prop.indexOf('__ember') < 0
&& prop.indexOf('_super') < 0
&& Ember.typeOf(model.get(prop)) !== 'function'
){
props[prop] = model[prop];
}
}
return props;
}
Neither of these answers are reliable, unfortunately, because any keys paired with a null or undefined value will not be visible.
e.g.
MyClass = Ember.Object.extend({
name: null,
age: null,
weight: null,
height: null
});
test = MyClass.create({name: 'wmarbut'});
console.log( Ember.keys(test) );
Is only going to give you
["_super", "name"]
The solution that I came up with is:
/**
* Method to get keys out of an object into an array
* #param object obj_proto The dumb javascript object to extract keys from
* #return array an array of keys
*/
function key_array(obj_proto) {
keys = [];
for (var key in obj_proto) {
keys.push(key);
}
return keys;
}
/*
* Put the structure of the object that you want into a dumb JavaScript object
* instead of directly into an Ember.Object
*/
MyClassPrototype = {
name: null,
age: null,
weight: null,
height: null
}
/*
* Extend the Ember.Object using your dumb javascript object
*/
MyClass = Ember.Object.extend(MyClassPrototype);
/*
* Set a hidden field for the keys the object possesses
*/
MyClass.reopen({__keys: key_array(MyClassPrototype)});
Using this method, you can now access the __keys field and know which keys to iterate over. This does not, however, solve the problem of objects where the structure isn't known before hand.
I use this:
Ember.keys(Ember.meta(App.YOUR_MODEL.proto()).descs)
None of those answers worked with me. I already had a solution for Ember Data, I was just after one for Ember.Object. I found the following to work just fine. (Remove Ember.getProperties if you only want the keys, not a hash with key/value.
getPojoProperties = function (pojo) {
return Ember.getProperties(pojo, Object.keys(pojo));
},
getProxiedProperties = function (proxyObject) {
// Three levels, first the content, then the prototype, then the properties of the instance itself
var contentProperties = getPojoProperties(proxyObject.get('content')),
prototypeProperties = Ember.getProperties(proxyObject, Object.keys(proxyObject.constructor.prototype)),
objectProperties = getPojoProperties(proxyObject);
return Ember.merge(Ember.merge(contentProperties, prototypeProperties), objectProperties);
},
getEmberObjectProperties = function (emberObject) {
var prototypeProperties = Ember.getProperties(emberObject, Object.keys(emberObject.constructor.prototype)),
objectProperties = getPojoProperties(emberObject);
return Ember.merge(prototypeProperties, objectProperties);
},
getEmberDataProperties = function (emberDataObject) {
var attributes = Ember.get(emberDataObject.constructor, 'attributes'),
keys = Ember.get(attributes, 'keys.list');
return Ember.getProperties(emberDataObject, keys);
},
getProperties = function (object) {
if (object instanceof DS.Model) {
return getEmberDataProperties(object);
} else if (object instanceof Ember.ObjectProxy) {
return getProxiedProperties(object);
} else if (object instanceof Ember.Object) {
return getEmberObjectProperties(object);
} else {
return getPojoProperties(object);
}
};
In my case Ember.keys(someObject) worked, without doing someObject.toJSON().
I'm trying to do something similar, i.e. render a generic table of rows of model data to show columns for each attribute of a given model type, but let the model describe its own fields.
If you're using Ember Data, then this may help:
http://emberjs.com/api/data/classes/DS.Model.html#method_eachAttribute
You can iterate the attributes of the model type and get meta data associated with each attribute.
This worked for me (from an ArrayController):
fields: function() {
var doc = this.get('arrangedContent');
var fields = [];
var content = doc.content;
content.forEach(function(attr, value) {
var data = Ember.keys(attr._data);
data.forEach(function(v) {
if( typeof v === 'string' && $.inArray(v, fields) == -1) {
fields.push(v);
}
});
});
return fields;
}.property('arrangedContent')