SilverStripe 3: Get ID of Parent DataObject in child's onAfterWrite() - silverstripe

In SilverStripe 2.4, I was able to get ID of parent DataObject "A" in onAfterWrite() method of child dataobject "B" by
$this->AClassID
How to get "A"'s ID in "B"'s onAfterWrite() method ?
Note:"A" has many "B"s and "B" has one "A" (one-to-many relationship).
Sample code here for kind consideration:
http://www.sspaste.com/paste/show/507d5222878a7

Try $this->AClass->ID. Or if you have a Parent relationship setup in your has_one ie
public static $has_one=array(
'Parent'=>'AClass'
);
you could use $this->Parent->ID.

Use if statement to make sure it is not 0 before continuing. like
if($this->ID){
//put all your code here
}
Or
if($this->RelationName()->ID){
//put all your code here
}
The reason is that it is called multiple times and you just need to execute when it is non-zero.

Try this one:
$this->Parent()->ID

Related

Symfony - Dynamically edit property

Imagine I have a "Product" Class that has 3 properties (creationTimestamp, name and price).
I do not want the user to change the name once the object has been created. No problem for that: I added an Event Subscriber to the FormType.
In addition, I do never show the creationTimestamp in my create and edit forms. I want this field to be populated "in the background" only when I create the object. It should not change when I edit my object. Question: should I put the logic in the Controller or somewhere else (ie. a Lifecycle Callback). What is the best practice ? A syntax example would be really appreciated. Thank you in advance
You could do it in the constructor.
class Product
{
public function __construct()
{
$nowTimestamp = 123456789; //Here your timestamp value
$this->setCreationTimestamp($nowTimestamp); //Assuming that you have defined setter function
}
}

Using the super method

I want to program with the Method super() in Dynamics AX2012.
I have build a class ("A") and some methods in it. I build another class("B") whhich extends from the class before.
My class "A" has some Methods.
Now in Class "B" I want to override a Method. I will do so.
I will override the Method getTable().
protected SYCCarBrandTable getTable()
{
SYCCarBrandTable ret;
ret = super();
{
select brandid,branddescription from ret
where ret.brandid == "Bentley";
}
return ret;
}
Now my Question is...
I have understood that with super() this new method did take everything with it, from the method which it Extended from in the motherclass "A".
But how can I add more Things to the method, so that it gives me the Things from the method before and the Things I have added in the overriden method ?
Looking at the implementation of getTable(), looks like you may want to select some SYCCarBrandTable record using the values of another SYCCarBrandTable returned by super() as criteria + other newly added criteria.
I am not sure why you would like to do such a thing, but if you perform the select statement upon the same table variable, you are actually really overriding all behavior, not adding anything.
If I got it right, you may want to use another SYCCarBrandTable:
protected SYCCarBrandTable getTable()
{
SYCCarBrandTable superCar;
SYCCarBrandTable ret;
superCar = super();
select brandid,branddescription from ret
where ret.CriteriaA = superCar.CriteriaA
&& ret.brandid == "Bentley";
return ret;
}
Then again, I am not sure why such a thing would be useful, but this is one sample way of aggregating functionality instead of fully overriding it.
If you want to add additional critria to a select you should use query object and design your select. Than you should create a new method on your base class called something like modifieQuery() or setQueryRange(). In this method you add range to your query as you want for base class. When you override this method in your class "B" you call super and than add additional ranges or simply override method without super() and just set ranges as needed. In your getTable method on base class you call modifieQuery() and execute it.

Silverstripe 3 - Detect changes on many_many relation

I've got dataobjects called attribute and attribute set
The attribute set has this many_many relation to attribute
private static $many_many = array(
'Attributes' => 'Attribute'
);
on attribute I've got this
private static $belongs_many_many = array(
'Sets' => 'AttributeSet'
);
You can add attributes to an set either directly from the set or on the attribute.
Now I need to know when a new attribute is added to a set, to update another content afterwards. I tried it with
public function onBeforeWrite(){
parent::onBeforeWrite();
if( $this->isChanged('Attributes') ){
$this->Title = 'test';
}
}
on the attribute set, but like presumed, it doesn't work, because the set get's not written if a new attribute is added.
Is there a way to do this?
Thank you in advance
You can serialize in some way like json_encode the ManyManyList and store it in a private variable during the init stage, then you can deserialize it during the onBeforeWrite and check for differences.
It's not an efficient task, but I think it's the only way you have to achieve your goal.
Couldn't you do something like this?
public function onBeforeWrite(){
parent::onBeforeWrite();
foreach($this->Attributes() as $attribute) {
if($attribute->isChanged()) {
$this->Title = 'test';
break;
}
}
}
Update: I now realise that this will not work for objects that are deleted. Maybe it is an option to do things the other way around. So do an onBeforeDelete on the many_many objects that sets the field in the "parent(s)" and then saves it. You could even do this for onbeforeWrite as well...
update 2:
It is a little unclear what you want. Do you want to know if the many_many objects have changed, regardless of when this happens, or do you just want to know if they change during the current page load?
isChanged only works when you load the object from the database, and then change something during the same cycle. The remainder of the current execution cycle, isChanged will return true. The next cycle, the object is reloaded, and isChanged returns back to false.
If you want to know if something changed since the last time you opened the parent object, you should store it in the database itself, or in the parent object (also in the db). This is quite easy, by just changing the parent object(s) with a boolean flag, and then saving again. If you want to track changes you need to implement something like #g4b0 suggests, or maybe try to add versioning to your objects. But the latter would probably force you to do a lot of custom coding.

Flex: Nested tags in MXML == run method.... how to set this up?

I have a class called JDChart, and a class called JDLine. Inside JDChart there is a method called addLine() that expects 1 parameter of type JDLine. This is all good. but I want to be able to put this in XML Like this:
<JDChart>
<JDLine/>
<JDLine/>
<JDLine/>
</JDChart>
And for each JDLine nested in a JDChart in the MXML, I want the addLine() method to be called on the JDChart with the respective JDLine passed.
Does what I want to do make since? I am not sure how to set this up? I am assuming I have to use meta tags on the JDChart class somewhere to tell the compiler to do this? Does anyone know?
Thanks!!
I believe when you add things in MXML like that it will just construct them and then call addChild().
You could have JDChart override addChild(), and check the type of what's being added. If it's a JDLine you can then pass it to your addLine() method before passing it along to super.addChild().
If JDLine objects are going to be parented only by JDChart objects, use this.
In the added event handler of the JDLine class, add the following code:
public function onAdded(e:Event):void
{
var chart:JDChart = this.parent as JDChart;
if(!chart)
throw new Error("Parent is not JDChart");
chart.addLine(this);
}

How can I identify an Axapta class Name from the class ID?

Please can someone help me make sense of the Batch madness?
I'm trying to debug an Axapta 3.0 implementation that has about 50 Batch Jobs. Most of the batched classes do not implement the description() method, so when you look at the Batch List form (Basic>>Inquiries>>Batch list) the description field is blank. You can see the Batch Group and the Start Time, etc. but you can't tell which class is actually being called.
The Batch table contains a hidden field called ClassNum which identifies the ID property of the class. Can anyone tell me how I can find the corresponding class from the ID? Once I've identified the culprits I can add descriptions.
I tried using the standard Find function on the AOT but it doesn't pick them up.
Any suggestions would be most welcome!
Many thanks,
Mike
Jay's answer provides two comprehensive solutions.
I've just discovered that the global class ClassId2Name does the same thing, so you can simply have:
display str Classname()
{
return ClassId2Name(this.ClassNum);
}
There atleast two ways to do this, you can use the DictClass class:
display ClassName className()
{
DictClass dictClass = new DictClass(this.ClassNum);
;
if(dictClass!=null)
return dictClass.name();
return '';
}
Or using the UtilIdElements table:
display ClassName className()
{
UtilIdElements utilIdElements;
;
select utilIdElements where utilIdElements.id==this.ClassNum && utilIdElements.recordType==UtilElementType::Class;
if(utilIdElements)
return utilIdElements.name;
return '';
}
Alternative to get ClassName if ClassNum is not available.
display str Classname()
{
return classId2Name(ClassIdGet(this));
}

Resources