Skip to content

Commit

Permalink
Merge pull request #12 from hchokshi/master
Browse files Browse the repository at this point in the history
Refactor, add support for upload fields, change to SS4 standards
  • Loading branch information
stevie-mayhew authored Jul 16, 2018
2 parents 0eb20a1 + dcbdf2a commit 0ee2cc8
Show file tree
Hide file tree
Showing 10 changed files with 314 additions and 119 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/.idea/
/vendor/
/resources/
/composer.lock
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,21 @@ For example, say you have a `has_one` called `Show` and that `has_one` has a fie

If you do not require that the outputted name of the field matches the value you supply, you can also use a colon as a separator instead of `-_1_-`.

### Generating fields with the `ProvidesHasOneInlineFields` trait

If you simply want to display all the CMS fields for a related object, you can add the `ProvidesHasOneInlineFields` trait to the object. This adds a method which calls `getCMSFields()`
on your `DataObject` and return the `FormField`s for that object. Those `FormField`s will be converted for use with this module by adding the relation name and separator to their name.

In the owning object, where you want to display the fields, call `HasOneEdit::getInlineFields($this, 'my_has_one_name')`. This will return the fields for adding to the CMS - e.g. you
can display the related object's fields in their own tab by calling `$fields->addFieldsToTab('Root.RelatedObject', HasOneEdit::getInlineFields($this, 'Relation'))`.

This has the advantage of running the entire `getCMSFields()` call tree (e.g. `updateCMSFields` for any functionality provided via extension) etc. without having to repeat logic
in a lot of places.

You can also implement a method `public function provideHasOneInlineFields($relationName)` returning `FieldList|FormField[]` to provide a custom interface different
to `getCMSFields()` (e.g. just a small subset of fields). In this case, all the field names should be in the form `$relationName . HasOneEdit::FIELD_SEPARATOR . $dataObjectFieldName`.
This method will be called by `HasOneEdit::getInlineFields` even if your class does not use the `ProvidesHasOneInlineFields` trait.

### Using with your own form

To add support to your own forms, you need to add the `SGN\HasOneEdit\UpdateFormExtension` extension to your controller and call `$this->extend('updateEditForm', $form)` before returning the form to the template. Without this, the fields will not get populated with the values from the `has_one` though saving will work.
54 changes: 0 additions & 54 deletions code/DataObjectExtension.php

This file was deleted.

62 changes: 0 additions & 62 deletions code/UpdateFormExtension.php

This file was deleted.

7 changes: 4 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stevie-mayhew/hasoneedit",
"type": "silverstripe-module",
"type": "silverstripe-vendormodule",
"description": "Allows editing the fields of a has_one object directly in the CMS",
"keywords": ["silverstripe", "has_one", "cms"],
"license": "WTFPL",
Expand All @@ -10,13 +10,14 @@
"homepage": "http://simon.geek.nz"
}],
"require": {
"silverstripe/framework": "~4.0.0-alpha3"
"silverstripe/framework": "^4.0",
"silverstripe/asset-admin": "^1.0"
},
"replace" : {
"simonwelsh/hasoneedit": "*"
},
"autoload": {
"psr-4": {"SGN\\HasOneEdit\\": "code/"}
"psr-4": {"SGN\\HasOneEdit\\": "src/"}
},
"extra": {
"branch-alias": {
Expand Down
36 changes: 36 additions & 0 deletions src/DataObjectExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace SGN\HasOneEdit;

use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;

class DataObjectExtension extends DataExtension
{
/**
* @see \SilverStripe\ORM\DataObject::onBeforeWrite()
*/
public function onBeforeWrite()
{
$changed = $this->owner->getChangedFields();
$toWrite = [];

foreach ($changed as $name => $value) {
if (!HasOneEdit::isHasOneEditField($name)) continue;

list($relationName, $fieldOnRelation) = HasOneEdit::getRelationNameAndField($name);
$relatedObject = HasOneEdit::getRelationRecord($this->owner, $relationName);
if ($relatedObject === null) continue;

$relatedObject->setCastedField($fieldOnRelation, $value['after']);
if ($relatedObject->isChanged(null, DataObject::CHANGE_VALUE)) {
$toWrite[$relationName] = $relatedObject;
}
}

foreach ($toWrite as $relationName => $obj) {
$obj->write();
$this->owner->setField("{$relationName}ID", $obj->ID);
}
}
}
84 changes: 84 additions & 0 deletions src/HasOneEdit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace SGN\HasOneEdit;
use SilverStripe\ORM\DataObject;

/**
* Class HasOneEdit
* @package SGN\HasOneEdit
*/
class HasOneEdit
{
/**
*
*/
const FIELD_SEPARATOR = '-_1_-';

/**
*
*/
const SUPPORTED_SEPARATORS = [
self::FIELD_SEPARATOR,
':',
'/',
];

/**
* @param \SilverStripe\Forms\FormField|string $field
* @return string[] Array of [relation name, field on relation]
*/
public static function getRelationNameAndField($field)
{
if (!is_string($field)) {
$field = $field->getName();
}

return explode(static::FIELD_SEPARATOR, $field, 2);
}

/**
* @param \SilverStripe\ORM\DataObject $parent
* @param string $relationName
* @return \SilverStripe\ORM\DataObject|null
*/
public static function getRelationRecord(DataObject $parent, $relationName)
{
return array_key_exists($relationName, $parent->hasOne()) || array_key_exists($relationName, $parent->belongsTo(false))
? $parent->getComponent($relationName)
: null;
}

/**
* @param \SilverStripe\Forms\FormField|string $field
* @return bool
*/
public static function isHasOneEditField($field)
{
if (!is_string($field)) {
$field = $field->getName();
}

return boolval(strpos($field, static::FIELD_SEPARATOR));
}

/**
* @param string $fieldName
* @return string
*/
public static function normaliseSeparator($fieldName)
{
return str_replace(static::SUPPORTED_SEPARATORS, static::FIELD_SEPARATOR, $fieldName);
}

/**
* @param \SilverStripe\ORM\DataObject $parent
* @param string $relation
* @return \SilverStripe\Forms\FieldList|\SilverStripe\Forms\FormField[]
*/
public static function getInlineFields(DataObject $parent, $relation)
{
/** @var \SilverStripe\ORM\DataObject|\SGN\HasOneEdit\ProvidesHasOneInlineFields $relatedObject */
$relatedObject = static::getRelationRecord($parent, $relation);
return $relatedObject->provideHasOneInlineFields($relation);
}
}
95 changes: 95 additions & 0 deletions src/HasOneUploadField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
<?php

namespace SGN\HasOneEdit;

use SilverStripe\AssetAdmin\Forms\UploadField;
use SilverStripe\Assets\File;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectInterface;
use SilverStripe\ORM\Relation;

/**
* Class HasOneUploadField
* @package App\Forms
*/
class HasOneUploadField extends UploadField
{
/**
* @var null|bool
*/
private $hasOneMultiUpload = null;

/**
* HasOneUploadField constructor.
* @param \SilverStripe\AssetAdmin\Forms\UploadField $original
*/
public function __construct(UploadField $original)
{
if (!HasOneEdit::isHasOneEditField($original)) {
throw new \InvalidArgumentException('Original upload field passed to HasOneUploadField must have the has_one separator "' .
HasOneEdit::FIELD_SEPARATOR . '" in its name.');
}

parent::__construct($original->getName(), $original->title, $original->getItems());

// Copy state from original upload field
foreach (get_object_vars($original) as $prop => $value) {
$this->{$prop} = $value;
}
}

/**
* Check if allowed to upload more than one file
* @see \SilverStripe\AssetAdmin\Forms\UploadField::getIsMultiUpload()
* @return bool
*/
public function getIsMultiUpload()
{
if ($this->hasOneMultiUpload === null) {
// Guess from record
list($relationName, $fieldOnRelation) = HasOneEdit::getRelationNameAndField($this);
$relatedObject = HasOneEdit::getRelationRecord($this->getRecord(), $relationName);

// Multi-upload disabled for has_one components
$this->hasOneMultiUpload = !($relatedObject && DataObject::getSchema()->hasOneComponent($relatedObject, $fieldOnRelation));
}

return $this->hasOneMultiUpload;
}

/**
* @see \SilverStripe\AssetAdmin\Forms\UploadField::saveInto()
* @inheritDoc
*/
public function saveInto(DataObjectInterface $record)
{
list($relationName, $fieldOnRelation) = HasOneEdit::getRelationNameAndField($this);
$record = HasOneEdit::getRelationRecord($this->getRecord(), $relationName);

// Check type of relation
$relation = $record->hasMethod($fieldOnRelation) ? $record->$fieldOnRelation() : null;
if ($relation instanceof Relation) {
// has_many or many_many
$relation->setByIDList($this->getItemIDs());
} elseif ($class = DataObject::getSchema()->hasOneComponent($record, $fieldOnRelation)) {
// Get details to save
$idList = $this->getItemIDs();

// Assign has_one ID
$id = !empty($idList) ? reset($idList) : 0;
$record->setField("{$fieldOnRelation}ID", $id);

// Polymorphic asignment
if ($class === DataObject::class) {
$file = $id ? File::get()->byID($id) : null;
$fileClass = $file ? get_class($file) : File::class;
$record->{"{$fieldOnRelation}Class"} = $id ? $fileClass : null;
}

// Write has one record
$record->write();
}

return $this;
}
}
Loading

0 comments on commit 0ee2cc8

Please sign in to comment.