Override prestashop class to make public a protected method - overriding

I'm trying to extend the AdminImportController on Prestashop to make public the copyImg function. So i made my own simple class whose code is just this
class MyAdminImportController extends AdminImportControllerCore {
public static function copyImg($id_entity, $id_image = null, $url, $entity = 'products', $regenerate = true) {
return parent::copyImg($id_entity, $id_image, $url, $entity, $regenerate);
}
}
But i get this error:
Runtime Notice: Declaration of MyAdminImportController::copyImg() should be compatible with AdminImportControllerCore::copyImg($id_entity, $id_image = NULL, $url = '', $entity = 'products', $regenerate = true)
What am I doing wrong?

Sorry, i'm stupid. The declaration must be identical. I've copied the declaration from probably another version of prestashop. The problem was the $url='' parts

Related

assertResponseRedirects method with PHPUnit by passing the URI as a parameter

Can someone explain to me why when I use the assertResponseRedirects method with PHPUnit by passing the URI as a parameter:
class UserControllerTest extends WebTestCase
{
public function testUnauthenticatedIsRedirected(): void
{
$this->client->request('GET', '/account');
static::assertResponseRedirects('/login');
}
}
I have this error:
Failed asserting that Symfony\Component\HttpFoundation\RedirectResponse Object &0000000044abc158000000000cda95f8 (
'targetUrl' => 'http://localhost/login'
...
But if I put the absolute URL, the test works:
public function testUnauthenticatedIsRedirected(): void
{
$this->client->request('GET', '/account');
static::assertResponseRedirects('http://localhost/login');
}
Everyone puts the URI and it works, but not me... I'm on Symfony 5.2.1
Thank you in advance
$this->client->request('GET', '/account');
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_FOUND, $response->getStatusCode());
$location = $response->headers->get('location');
$this->assertEquals('expected URL here', $location);

Complex serialization

For example, I have two entities: main (parent) entity (for example User) and dependent entity (Post). I want to serialize User entity using JMS Serializer with additional information of its first post date. The post date stored in DB is timestamp int, but I want to serialize it with my helper (just a service) which converts int to sting with some formatting.
Trying to create a virtual_property with method at entity class, but failed to inject my helper into entity class. The only way to solve it for me is to serialize by myself into controller:
public function someAction()
{
$serializedUser = $this->serializeEntity($user);
}
public function serializeEntity(User $user)
{
// JMS serialization
$user = $this->get('jms_serializer')->serialize($user, 'array');
if ($user->getPosts()->count()) {
$post = $user->getPosts()->first();
$user['first_post_date'] = $this->get('my_helper_service')->dateFormat($post->getDate());
}
return $user;
}
NB: this example is synthetic, in the real world I have more complex methods, not just date formatter. But the main idea is the same.
I feel there should be better way to do this.
Dmtry's solution should work perfectly for your case.
An event listener/subscriber is the best solution in this case.
A bit more general solution, that works even with objects and will trigger the whole event system part of the JMS serializer (Dmtry's solution works only with primitives and only for JSON/YAML, not XML), is:
class MyFormatter implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return array(
array(
'event' => 'serializer.post_serialize',
'method' => 'onPostSerialize',
'class' => 'YourEntity'
),
);
}
public function __construct(MyFormatter $foermatter)
{
$this->formatter = $formatter;
}
public function onPostSerialize(ObjectEvent $event)
{
$visitor = $event->getVisitor();
$context = $event->getContext();
$timestamp = $event->getObject()->getTimestamp();
$formattedTime = $this->formatter->format($timestamp);
$metadata = new StaticPropertyMetadata('stdClass', 'first_post_date', $formattedTime);
$visitor->visitProperty($metadata, $formattedTime, $context);
}
}
stdClass is ignored by the serializer...
Of course, there is a better way. It's called serialization events.
http://jmsyst.com/libs/serializer/master/event_system
You create event subscriber
my_bundle.serializer_subscriber:
class: MyBundle\Serializer\MyEntitySerializerSubscriber
arguments:
- #bundle.time_formatter
tags:
- { name: jms_serializer.event_subscriber }
And then just add the data you need in your listener
public function myOnPostSerializeMethod(ObjectEvent $event)
{
if (!($event->getObject() instance of YourEntity)) {
return;
}
$timestamp = $event->getObject()->getTimestamp();
$visitor = $event->getVisitor();
$visitor->addData('date', $this->formatter->format($timestamp));
}
P.S. I didn't check the code, so maybe I'm mistaken somewhere with name of methods, but the idea is clear, I hope

Customize ModelAdmin listing in SilverStripe

Is it possible to change or add custom summary_fields into a listing from a ModelAdmin extends? Actually I'm able to filter a custom field named Type but I don't know how to customize the summary_fields. This is my actual code:
class Profiles3ModelAdmin extends ModelAdmin {
public static $menu_icon = 'mysite/images/peoples.png';
public static $managed_models = array('Member');
public static $url_segment = 'membres';
public static $menu_title = 'Membres';
public function getList() {
$group = Group::get()->filter('Code', array('Membres'))->first();
$list = $group->Members()->filter('Type', 1 );
return $list;
}
}
Your current query should be able to use $list = Member::get()->filter(array('Groups.Code' => 'Membres', 'Type' => 1)); if I recall correctly. Just gets it down to one line.
Usually to add to the summary you would add it to your class's model. So in this case on Member you would apply a DataExtension that had:
<?php
class MyMemberDataExtension extends DataExtension{
private static $summary_fields = array(
'Type'
);
}

Add new Page using GridField - creates the child in root folder

I would like to use GridField to view and create new child pages. Parent is DocumentHolder, child is Document. Both extend SiteTree. When I click to "Add Document" (button generated by grid), fill in the fields and confirm the form, the parent page is ignored and the page is created in root. It works well when I use DataObject. The code looks like this:
class DocumentHolder extends SiteTree
{
private static $allowed_children = array(
'Document'
);
private static $default_child = "Document";
public function getCMSFields()
{
$fields = parent::getCMSFields();
$gridField = new GridField('Documents', 'Documents', SiteTree::get('Document')->filter('ParentID', $this->ID), GridFieldConfig_RecordEditor::create());
$fields->addFieldToTab("Root.Uploads", $gridField);
return $fields;
}
}
class Document extends SiteTree
{
private static $db = array(
);
private static $has_one = array(
);
}
Thanks for help.
Since SiteTree already has a relationship to its Children pages set up, you may as well use it! Since allowed_children will only ever be documents, try this instead:
$gridField = new GridField('Documents', 'Documents', $this->Children(), GridFieldConfig_RecordEditor::create());
I ran into this problem earlier working on my holderpage module. You need to set the ParentID by default. Here's two strategies;
You can use populateDefaults on the child class. E.g.
class Document extends SiteTree
{
private static $default_parent = 'DocumentHolder';
private static $can_be_root = false;
public function populateDefaults(){
parent::populateDefaults();
$this->ParentID = DataObject::get_one(self::$default_parent)->ID;
}
...
Or you can manipulate the record in the gridfield with a custom GridFieldDetailForm implementation or via the updateItemEditForm callback.
<?php
class MyGridFieldDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
public function ItemEditForm()
{
$form = parent::ItemEditForm();
if (! $this->record->exists() && $this->record->is_a('SiteTree')) {
$parent_page = $this->getController()->currentPage();
if ($parent_page && $parent_page->exists()) {
$this->record->ParentID = $parent_page->ID;
// update URLSegment #TODO perhaps more efficiently?
$field = $this->record->getCMSFields()->dataFieldByName('URLSegment');
$form->Fields()->replaceField('URLSegment', $field);
}
}
return $form;
}
}
This is more complicated although it allowed me to create an effortless module / addon ( https://github.com/briceburg/silverstripe-holderpage )

Customising the CMS

I'm having trouble with how to do something similar to this http://www.silverstripe.org/archive/show/2431. Basically I want the user to be able to create content and have their ID put into the database with the new content. I'm sorry if this is fairly obvious. I am a little stumped though. I am having trouble wrapping my head around how it will actually work. I know I can retrieve the current user using the following code but I'm not sure where to go from there.
$currentUser = Member::currentUser();
In SilverStripe usually the 3rd argument to a FormField is the value, so for example a TextField has the following arguments:
new TextField($name = 'myField', $title = 'Please write something in my Field', $value = "yay");
but this would not work in the CMS (at least in SilverStripe 2 if you are using a Page, not sure on DataObject) because SilverStripe overwrites all values when it tries to populate the Form with the values of the current object
so you have several alternatives, the 2 easiest alternatives are:
class MyContentObject extends DataObject {
public static $db = array(
'Text' => 'HTMLText',
);
public static $has_one = array(
'Member' => 'Member',
)
public function getCMSFields() {
$fields = new FieldSet();
$fields->push(new Textarea('Text', 'Text'));
if (!$this->MemberID)
$this->MemberID = Member::currentUserID();
$fields->push(new HiddenField('MemberID'));
return $fields;
}
}
And 2nd option, which is way better in this case, you don't even need a hidden field, you can just set the MemberID right before the record gets written to database by using onBeforeWrite:
class MyContentObject extends DataObject {
public static $db = array(
'Text' => 'HTMLText',
);
public static $has_one = array(
'Member' => 'Member',
)
public function getCMSFields() {
$fields = new FieldSet();
$fields->push(new Textarea('Text', 'Text'));
return $fields;
}
public function onBeforeWrite() {
// this method will be called every time the object gets saved
parent::onBeforeWrite();
if (!$this->MemberID)
$this->MemberID = Member::currentUserID();
}
}

Resources