Silverstripe 4 add GridField in cms tab - silverstripe

Dear Silverstripe 4 users, i need someone to explain me how to slove my problem. Am building ecommerce module, i create data object for insert new proucts and categories without any problems.
Probelm start when i try to create related products for new product. I do that from has_many relatian and ListboxField and that is work good but i dont want to do that with list box filed. I want to create new tab like (Root.RelatedProducts) and there to have gridfield where i can add existing product.
I know to create grid with headers, actions and other grid configuration. When i create grid i only can add new product.
How to implement to chose existing product and show in grid in specific tab?
class Product extends Page
{
private static $has_many = [
'RelatedProducts' => Product::class
];
public function getCMSFields()
{
$fields = parent::getCMSFields();
$gridFieldConfig = GridFieldConfig_RecordEditor::create();
$gridFieldConfig = GridFieldConfig::create()->addComponents(
new GridFieldToolbarHeader(),
new GridFieldAddNewButton('toolbar-header-right'),
new GridFieldSortableHeader(),
new GridFieldDataColumns(),
new GridFieldPaginator(10),
new GridFieldEditButton(),
new GridFieldDeleteAction(),
new GridFieldDetailForm()
);
$gridField = new GridField("RelatedProducts", "Related products", $products, $gridFieldConfig);
}
}

If you want to add existing products to the relation, you can use another gridfield config. Silverstripe has some predefined configs available and you took the Gridfield_RecordEditor.
If you use Gridfield_RelationEditor you also get a component to search for existing records. From the docblock of RelationEditor:
Similar to {#link GridFieldConfig_RecordEditor}, but adds features to
work on has-many or many-many relationships.
Allows to search for existing records to add to the relationship,
detach listed records from the relationship (rather than removing them
from the database), and automatically add newly created records to it.
You can configure it more like this:
GridFieldConfig_RelationEditor::create()
->getComponentByType('GridFieldAddExistingAutocompleter')
->setSearchFields('MyField');
By the way: I'd use a many_many relation, as a product could be related to more than one product.

Related

Silverstripe drop down field does not show saved value as selected

I have created a drop down field in the CMS like so:
class ProductPage extends Page {
//.....
private static $has_one = [
'TeaserImage'=>Image::Class,
'LinkedProduct'=>'Product'
];
public function getCMSFields(){
$fields = parent::getCMSFields();
$productLinkField = DropdownField::create('LinkedProduct', 'Link a Product', Product::get()->map('ID', 'ProductName'));
$productLinkField->setEmptyString('(Select one)');
$fields->addFieldToTab('Root.Main', $productLinkField, 'Content');
return $fields;
}
}
The problem is that when I select a value and save/publish the page the drop down goes back to "Select one" instead of showing the saved selection.
I have not checked the database to see if the value is being stored but I assume it is.
EDIT: Not a duplicate.
The suggested duplicate dealt with removing a field from the CMS.
This question deals with setting the drop down value to the saved selection.
The answers are however similar. The user must always append ID to a has_one field for the CMS to interact with it.
By default SilverStripe appends an 'ID' parameter to the end of has_one relation fields when saving them in the database.
As such when you override the field for relations you will need to append 'ID' to the field identifier.
DropdownField::create('LinkedProductID', 'Link a Product', Product::get()->map('ID', 'ProductName'));

CMIS add custom type to cm:folder

I've one repository where I create some Site for store all documents and folders that my organization create.
I've one web app that include this php library https://github.com/dkd/php-cmis-client (port of Apache Chemistry Java implementation).
So I can creates documents, folder, set some properties, etc... but I would like to do "more".
I would like to extend the model and create my own model. In this case, It's very simple, I create "myPersonal" model, and create one custom type 'folderAmp' (his parents is cm:folder). And I extend this custom type and create a new property "myP:idNew" where I want to store some id code.
So, When I create a folder with this library (or maybe in java) I create it with...
JAVA
Folder parent = ....
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(PropertyIds.NAME, "a new folder");
properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder");
// create the folder
Folder newFolder = parent.createFolder(properties);
PHP
$properties = array(
\Dkd\PhpCmis\PropertyIds::OBJECT_TYPE_ID => 'cmis:folder',
\Dkd\PhpCmis\PropertyIds::NAME => 'Demo Folder'
);
try {
$folder = $session->createFolder(
$properties,
$session->createObjectId($session->getRepositoryInfo()->getRootFolderId())
);
So if I want to add my new custom type... What I've to do?
I try to add with this:
properties.put(PropertyIds.OBJECT_TYPE_ID, "cmis:folder, myP:folderAmp" but it doesn't works.
You are looking for createType(). Not all repositories support it.
Get the CMIS Workbench from https://chemistry.apache.org/java/download.html , login into your repository, and open the "Types window". Select the folder type (cmis:folder). If the "Create Type" button on the top is enabled you can create a new subtype of cmis:folder. If not, either the repository doesn't support it or you don't have the permissions to do this.
But...if create type button is not enable... this repository is not "CMIS standar" ¿no?
Finally, the problem I solved it...very simple... I change
$properties = array(
\Dkd\PhpCmis\PropertyIds::OBJECT_TYPE_ID => 'cmis:folder',
\Dkd\PhpCmis\PropertyIds::NAME => 'Demo Folder'
);
try {
$folder = $session->createFolder(
$properties,
$session->createObjectId($session->getRepositoryInfo()->getRootFolderId())
);
By this...
$properties = array(
\Dkd\PhpCmis\PropertyIds::OBJECT_TYPE_ID => 'F:oto:Historia',
\Dkd\PhpCmis\PropertyIds::NAME => 'Demo Folder'
);
try {
$folder = $session->createFolder(
$properties,
$session->createObjectId($session->getRepositoryInfo()->getRootFolderId())
);
Where oto it's a model and Historia it's a custom type that I create with Alfresco Console Admin. I included all parent (cm:folder) properties in oto:Historia and create same custom properties.
When I create the folder, it's create with oto:Historia type.

drupal 8 querying all entitys of a type i just made

I just made an entity 'car'
I have added 10 of these entities in the drupal cms.
Now i want all ID's of the entitys 'car' in my controller
This is what i tried so far that did not work:
$query = \Drupal::entityQuery('car');
$query->condition('status', 1);
$ids = $query->execute();
$entity_query = \Drupal::service('entity.query')->get('car');
$uids = $entity_query->execute();
Both give me much more then just the 'car' entity.
they deliver other content types as well
Can someone help me how i can get all published 'car' nodes (id's)
so i can get all info for them with the load() command?
tnx
Problem solved:
Create an instance of the entity.
Then use that instance to build the entityForm
$entity = $this->entityTypeManager()->getStorage('car')->create(array());
$form = \Drupal::service('entity.form_builder')->getForm($entity,'default');
(I did create the entity using the drupal console and the default form you can set in the annotation section form in the entity file)

Drupal - Importing a taxonomy with migrate module from a table and creating/updating existing terms

I need to import a list of terms into my taxonomy from a source I loaded in the database.
The problem is I allready have this taxonomy on my site (loaded wihtout migrate) with terms that are used by reference in other content, so I got to keep existing term and update them or create the new ones.
To link my taxonomy source and the existing taxonomy I have an unique code for each term, so I added a code field to my vocabulary and filled it for each existing term.
I am currently able to create and update terms with my current Migration class, but if the name of my term on the site and the name of the term in my source is different, the import will create a new term instead of updating its name even if the code is the same.
Here my Migration Class :
class TotoMigration extends Migration {
private $list_term = array();
public function __construct($arguments) {
parent::__construct();
$this->softDependencies = array('TotoParent');
// get data from the custom table containing the new terms to create or update
$query = db_select('toto', 'f')
->fields('f', array(
'CODE', // code
'LIBLONG', // name
'PARENT', // parent
)
);
$this->source = new MigrateSourceSQL($query);
$this->destination = new MigrateDestinationTerm('toto_tax');
$this->map = new MigrateSQLMap($this->machineName,
array(
'CODE' => array('type' => 'varchar',
'length' => 5,
'not null' => TRUE,
'description' => 'Code',
)
),
MigrateDestinationTerm::getKeySchema()
);
$this->addFieldMapping('name', 'LIBLONG');
$this->addFieldMapping('field_code', 'CODE');
$this->addFieldMapping('parent', 'PARENT')
->arguments(array('source_type' => 'tid'))
->sourceMigration('TotoParent');
// create a list of existing toto terms with code => tid
$list_term = db_query("select fc.field_code_value, ttd.tid
from taxonomy_term_data ttd
left join taxonomy_term_hierarchy tth on tth.tid=ttd.tid
left join field_data_field_code fc on fc.entity_id = ttd.tid
where ttd.vid=10
and tth.parent!=0;")->fetchAllKeyed();
}
public function prepareRow($row) {
// Always include this fragment at the beginning of every prepareRow()
// implementation, so parent classes can ignore rows.
if (parent::prepareRow($row) === FALSE) {
return FALSE;
}
// if the destination is not mapped in migrate we tell him where to go
if (!isset($row->migrate_map_destid1) && isset($list_term[$row->CODE])) {
$row->migrate_map_destid1 = $list_term[$row->CODE];
}
}
}
I then load the import with drush (and --update option).
I must be missing something, if anyone got a clue it will be welcome.
After many tries, the problem reside in the fact the module Migrate does not support Creating content and Updating content in the same migration class (I even read it will sometime claim to update content and just do nothing).
So the solution is pretty simple, create 2 classes :
One for Creating content
One for Updating content
Your Creating class will be the same.
Your Updating class will need to have a systemeOfRecord set to DESTINATION :
$this->systemOfRecord = Migration::DESTINATION;
So it knows to only update and not recreate the content, it will keep current fields not mapped and update fields mapped that are not part of the MigrateSQLMap :
$this->map = new MigrateSQLMap($this->machineName,array(...));
The tricky part will be to find corresponding nid/tid of your content so you can map it to your imported data and then to separate data used to update or create content.

SilverStripe GridField: too many versions of DataObject get created

TL;DR When creating/saving a versioned DataObject with relation to some page, two entries are created in the corresponding versions table (instead of one).
I'm trying to version some DataObject and have the Versioned extension applied as follows:
class Testobject extends DataObject {
static $has_one = array(
'Page' => 'Page'
);
static $extensions = array(
"Versioned('Stage', 'Live')",
);
Testobjects are being managed in a GridField on some page like so:
class PageContent extends Page {
public static $has_many = array(
"Testobjects" => "TestObject"
);
public function getCMSFields() {
$fields = parent::getCMSFields();
$config = GridFieldConfig_RelationEditor::create();
$gridField = new GridField(
'Testobjects',
'Testobject',
$this->Testobjects(),
$config);
$fields->addFieldToTab('Root.Main', $gridField);
}
Now, whenever i add or save some Testobject in the GridField's EditForm, two new entries show up in the Testobject_versions table. For comparsion, when i save a page in the SiteTree, only one entry in the corresponding versions table is created.
As there will we thousands of these DataObjects on a page, i'm worried about this duplication filling up my database. Is there a way to get around this?
Further recognitions:
On creation of a new Testobject, the first entry in the versions table has it's PageID field set to 0, the second entry has set the actual PageID of the corresponding page.
If I replace $this->Testobjects() in the GridField construction by Testobject::get(), only one entry shows up in the versions table.
onBeforeWrite is called twice when using $this->Testobjects()
So it seems setting the relation to the page happens after a first 'write()', then another 'write()' is called. But where in the code does this happen?
If you're editing your page/testobject in the main section of the CMS (# '/admin/pages'), you can try this somewhat hackish trick in your TestObject class
public function getCMSFields(){
$fields = parent::getCMSFields();
$fields->push( new HiddenField('PageID','PageID', Controller::curr()->CurrentPageID());
return $fields;
}
This is not ideal for the following reasons:
hard to test with unit test controller
awareness of the controller in the model is bad
IMHO
But it can be a reasonable fix if it works for you

Resources