Getting $this->File() returns empty class - silverstripe-4

I have a simple SilverStripe 4.0 DataObject that has an image (ie. $has_one = array("Image" => Image:class)). When attempting to get this object's Image I get an empty Image object instead.
For example:
function DoStuff {
return $this->ImageID; // returns relationship ID (eg 123)
return $this->Image()->ID; // returns NULL
return $this->Image()->URL; // returns NULL
}
What on earth is going on?! I'm savvy with SilverStripe 3, so am stumped by this issue which is isolated to SilverStripe 4.

Alas! It appears to be that whenever you upload (or migrate) assets they default to being unpublished. This is sensible, but not documented anywhere.
This is the reason why I was getting an empty File record returned - the expected record was unpublished.

Related

Add Auth::user() to Eloquent save (); request

How can I write the two lines of code below into one line of code:
$service_review->user_id=\Auth::user();
$user_service->service_reviews()->save($service_review);
The reason being that the line including Auth::user() is throwing an error since it's a foreign key in my "service_reviews" table and so "DOESN'T HAVE A DEFAULT VALUE"
The problem is if I give precedence to:
\Auth::user()->service_reviews()->save($service_review);
Then in this case, the authorized user is fetched but my user_service_id now throws the error as "DOESN'T HAVE A DEFAULT VALUE".
The code of my store method (This is from my ReviewsController that is based on a Nested Route:
Route::resource("services.reviews", "ReviewsController"); is as follows:
public function store(ReviewsRequest $request, $id){
$service_review = new Service_review($request->all());
$user_service = User_service::findOrFail($id);
$service_review->user_id=\Auth::user();
$user_service->service_reviews()->save($service_review);
return redirect("reviews");
I reckon passing them in one line of code will solve this error.
Yes your're right #MinaAbadir
I ended up doing it as follows:
public function store(ReviewsRequest $request, $id){
$service_review = new Service_review($request->all());
$user_service = User_service::findOrFail($id);
$service_review->user_id = \Auth::user()->email;
$user_service->service_reviews()->save($service_review);
return redirect("reviews");
}
It seems calling the specific column you want retrieved negates the foreign key problem. Maybe MySQL figures out the relationship via Eloquent. Let me know if I can clean this up further.

Clearing form collection entirely

I have an issue with Symfony 2.6 form collections. Removing elements from collection works, however only when at least one is present. If the last element is removed from DOM (leaving the collection container empty), no elements are removed from the collection after handling request.
Example:
I have a form with collection "children" and two children, "a" and "b". I remove the child "b" from DOM, save, removeChild is called, the child is removed. Now I also remove child "a", save, nothing happens - after refreshing the form the child is still present.
When dumping the main entity after the form has handled request, the child is present in it's collection as well.
Did anyone have similar problem and found a solution?
Thanks to #Daniel pointing me in a new direction I have found a solution. The method submit does in fact accept second argument - clear empty. Passing request to submit is however deprecated and will be removed in Symfony 3.0.
Handle request does in fact support the clear empty feature.
It is simply not passed manually, but based on the request method. If the method is post, clear empty is set to true. If method is patch, clear empty is false. In my case the method was patch, hence the issue.
You can do this by 2 methods :
as in symfony doc : form collection doc
or with doctrine annotation : doctrine orphanRemoval doc
But don't forget cascade={"remove"} annotation
You can delete all the items of a Collection of an Entity simply by:
$request['collectionName'] = null; //or empty array
$form->submit($request, false);
Problems arise when this $request comes from a javascript Ajax call:
var item = {field: 'test', collectionName: null};
ajaxPatchRequest(item);
since the null value is received as String "null". If collectionName is an empty array it will not be passed within the ajax call.
Thus, use the null value and apply a preprocessing before $form->submit():
$toPatch = array();
foreach($request->request->all() as $key => $value) {
if($value === 'null') {
$toPatch[$key] = null;
} else {
$toPatch[$key] = $value;
}
}
$form->submit($toPatch, false);

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.

Passing param to flex module via url

Im using a few modules repeatedly in a Flex app and varying the conditions by passing in params via the 'loaderInfo.url' var. This works fine for the first iteration of a given module but subsequent attempts will always see the same params as the first creation regardless of what is actually used.
Is there some way to reset this value when the module is created?
private var moduleInfo : IModuleInfo;
private function loadPageModule( pathString : String, pageParam : String ) : void
{
pathString = "modules/" + pathString + ".swf?param=" + pageParam;
moduleInfo = ModuleManager.getModule( pathString );
moduleInfo.addEventListener( ModuleEvent.READY, onPageModuleReady, false, 0, true);
moduleInfo.load( ApplicationDomain.currentDomain, null, null );
}
When I view the param in the 'CreationComplete' handler (eg 'trace( this.loaderInfo.url );') its the same every time (for a given module) regardless of what is actually passed in via the ?param=string. What am I doing wrong?
I'm little confused by your code. "moduleInfo" is a local variable in loadPageModule, meaning after the function executes it should be garbage collected. Typically adding a event listener would stop the object from being GC'ed but you specifically set the "weakListener" argument of addEventListener to true.
Can you set the weakListener argument of addEventListener to false and manually remove the listener in onPageModuleReady? Like below:
// change true to false on this line
moduleInfo.addEventListener( ModuleEvent.READY, onPageModuleReady, false, 0, false);
// tweak onPageModuleReady
private function onPageModuleReady(e:Event):void {
e.target.removeEventListener(ModuleEvent.READY, onPageModuleReady);
// that will allow the object to be GC'ed
...rest of your code
Test it again with that configuration and report back if things change.
Well I don't know why this is doing what its doing.. but.. an effective way to pass unique params to different instances of a module is to use the IModuleInfo.data param. The url method never worked properly (for me).

Showing default widget value only when object value is not set (in NEW mode)

THIS QUESTION IS NOT ABOUT HOW TO SET DEFAULT VALUE OF A WIDGET
Hello Symfonians!
I had a fundamental doubt about forms, Im putting 2 scenarios below.
I have a customModelForm that extends a modelForm.
1> If I do not specify a default value for a form field
new: field is empty
edit: field shows the value in the object
2> If I specify a default value for a field,
new: field shows default value
edit: field shows default value
I am trying to avoid the EDIT mode behaviour in scenario 2.
The default value should only be displayed when the value in the object is not set.
I am calling parent::configure after setting the default value. Do we have any control on the 'bind' event?
Thanks
This shouldn't be happening, at least in Doctrine. The part of the code where this is happening is in updateDefaultsFromObject in sfFormDoctrine. The relevant lines are:
if ($this->isNew())
{
$defaults = $defaults + $this->getObject()->toArray(false);
}
else
{
$defaults = $this->getObject()->toArray(false) + $defaults;
}
updateDefaultsFromObject does net get called until the entire configure chain is done, so something else must be going on here.
Are you using Doctrine? Are you using the most current version of Symfony (there was a bug here a while ago)? Are you sure the default is getting set in the configure method of your form?
The isNew check richsage is recommending should be avoided. There is a larger issue here as the proper behavior is for default value to get overwritten by an existing object's values.
First of all, call parent::configure() first in your configure() method. That way you don't run the risk of your configuration being overwritten by the parent configuration.
You can set defaults based on the model's status by doing something like the following in your configure() method:
if ($this->getObject()->isNew())
{
// do something here but only if the object is new
}
else
{
// the object is being edited
}

Resources