Skip to content

Commit

Permalink
Merge pull request tractorcow-farm#303 from creative-commoners/pulls/…
Browse files Browse the repository at this point in the history
…4.0/localised-fields

Individual fields whitelisting option added.
  • Loading branch information
Damian Mooyman committed Nov 10, 2017
2 parents db86260 + 3e7e3f1 commit 597319b
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 26 deletions.
105 changes: 79 additions & 26 deletions src/Extension/FluentExtension.php
Expand Up @@ -23,17 +23,27 @@
/**
* Basic fluent extension
*
* When determining whether a field is localised, the following config options are checked in order:
* - translate (uninherited, for each class in the chain)
* - field_exclude
* - field_include
* - data_exclude
* - data_include
*
* @property FluentSiteTreeExtension|DataObject $owner
*/
class FluentExtension extends DataExtension
{
/**
* The table suffix that will be applied to create localisation tables
*
* @var string
*/
const SUFFIX = 'Localised';

/**
* translate config key to disable localisations for this table
*/
const TRANSLATE_NONE = 'none';

/**
* DB fields to be used added in when creating a localised version of the owner's table
*
Expand Down Expand Up @@ -63,15 +73,29 @@ class FluentExtension extends DataExtension
];

/**
* Filter whitelist of fields to localise
* List of fields to translate for this record
*
* Can be set to a list of fields, or a single string 'none' to disable all fields.
* Not inherited, and must be set per class.
*
* If set takes priority over all white / black lists
*
* @var array|string
*/
private static $translate = [];

/**
* Filter whitelist of fields to localise.
* Note: Blacklist takes priority over whitelist.
*
* @config
* @var array
*/
private static $field_include = [];

/**
* Filter blacklist of fields to localise
* Filter blacklist of fields to localise.
* Note: Blacklist takes priority over whitelist.
*
* @config
* @var array
Expand All @@ -85,6 +109,7 @@ class FluentExtension extends DataExtension

/**
* Filter whitelist of field types to localise
* Note: Blacklist takes priority over whitelist.
*
* @config
* @var
Expand All @@ -97,7 +122,8 @@ class FluentExtension extends DataExtension
];

/**
* Filter blacklist of field types to localise
* Filter blacklist of field types to localise.
* Note: Blacklist takes priority over whitelist.
*
* @config
* @var array
Expand Down Expand Up @@ -132,39 +158,66 @@ public function getLocalisedFields($class = null)
// List of DB fields
$fields = DataObject::getSchema()->databaseFields($class, false);
$filter = Config::inst()->get($class, 'translate', Config::UNINHERITED);
if ($filter === 'none' || empty($fields)) {
if ($filter === self::TRANSLATE_NONE || empty($fields)) {
return $this->localisedFields[$class] = [];
}

// Data and field filters
$fieldsInclude = Config::inst()->get($class, 'field_include');
$fieldsExclude = Config::inst()->get($class, 'field_exclude');
$dataInclude = Config::inst()->get($class, 'data_include');
$dataExclude = Config::inst()->get($class, 'data_exclude');

// filter out DB
foreach ($fields as $field => $type) {
// If given an explicit field name filter, then remove non-presented fields
if ($filter) {
if (!in_array($field, $filter)) {
unset($fields[$field]);
}
continue;
}

// Without a name filter then check against each filter type
if (($fieldsInclude && !$this->anyMatch($field, $fieldsInclude))
|| ($fieldsExclude && $this->anyMatch($field, $fieldsExclude))
|| ($dataInclude && !$this->anyMatch($type, $dataInclude))
|| ($dataExclude && $this->anyMatch($type, $dataExclude))
) {
if (!$this->isFieldLocalised($field, $type, $class)) {
unset($fields[$field]);
}
}

return $this->localisedFields[$class] = $fields;
}

/**
* Check if a field is marked for localisation
*
* @param string $field Field name
* @param string $type Field type
* @param string $class Class this field is defined in
* @return bool
*/
protected function isFieldLocalised($field, $type, $class)
{
// Explicit per-table filter
$filter = Config::inst()->get($class, 'translate', Config::UNINHERITED);
if ($filter === self::TRANSLATE_NONE) {
return false;
}
if ($filter && is_array($filter)) {
return in_array($field, $filter);
}

// Named blacklist
$fieldsExclude = Config::inst()->get($class, 'field_exclude');
if ($fieldsExclude && $this->anyMatch($field, $fieldsExclude)) {
return false;
}

// Named whitelist
$fieldsInclude = Config::inst()->get($class, 'field_include');
if ($fieldsInclude && $this->anyMatch($field, $fieldsInclude)) {
return true;
}

// Typed blacklist
$dataExclude = Config::inst()->get($class, 'data_exclude');
if ($dataExclude && $this->anyMatch($type, $dataExclude)) {
return false;
}

// Typed whitelist
$dataInclude = Config::inst()->get($class, 'data_include');
if ($dataInclude && $this->anyMatch($type, $dataInclude)) {
return true;
}

return false;
}

/**
* Get all database tables in the class ancestry and their respective
* translatable fields
Expand Down
59 changes: 59 additions & 0 deletions tests/php/Extension/FluentExtensionTest.php
Expand Up @@ -6,10 +6,21 @@
use SilverStripe\Dev\SapphireTest;
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
use TractorCow\Fluent\State\FluentState;
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedAnother;
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedChild;
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\LocalisedParent;
use TractorCow\Fluent\Tests\Extension\FluentExtensionTest\UnlocalisedChild;
use TractorCow\Fluent\Tests\Extension\Stub\FluentStubObject;

class FluentExtensionTest extends SapphireTest
{
protected static $extra_dataobjects = [
LocalisedAnother::class,
LocalisedChild::class,
LocalisedParent::class,
UnlocalisedChild::class,
];

protected static $required_extensions = [
SiteTree::class => [
FluentSiteTreeExtension::class,
Expand Down Expand Up @@ -45,4 +56,52 @@ public function testGetLinkingMode()
FluentState::singleton()->setLocale('foo');
$this->assertSame('current', $stub->getLinkingMode('foo'));
}

public function testGetLocalisedFields()
{
// test data_include / data_exclude
// note: These parent fields should be all accessible from the child records as well
$parent = new LocalisedParent();
$parentLocalised = [
'Title' => 'Varchar',
'Details' => 'Varchar(200)',
];
$this->assertEquals(
$parentLocalised,
$parent->getLocalisedFields()
);

// test field_include / field_exclude
$another = new LocalisedAnother();
$this->assertEquals(
[
'Bastion' => 'Varchar',
'Data' => 'Varchar(100)',
],
$another->getLocalisedFields()
);
$this->assertEquals(
$parentLocalised,
$another->getLocalisedFields(LocalisedParent::class)
);

// Test translate directly
$child = new LocalisedChild();
$this->assertEquals(
[ 'Record' => 'Text' ],
$child->getLocalisedFields()
);
$this->assertEquals(
$parentLocalised,
$child->getLocalisedFields(LocalisedParent::class)
);

// Test 'none'
$unlocalised = new UnlocalisedChild();
$this->assertEmpty($unlocalised->getLocalisedFields());
$this->assertEquals(
$parentLocalised,
$unlocalised->getLocalisedFields(LocalisedParent::class)
);
}
}
25 changes: 25 additions & 0 deletions tests/php/Extension/FluentExtensionTest/LocalisedAnother.php
@@ -0,0 +1,25 @@
<?php

namespace TractorCow\Fluent\Tests\Extension\FluentExtensionTest;

use SilverStripe\Dev\TestOnly;

class LocalisedAnother extends LocalisedParent implements TestOnly
{
private static $table_name = 'FluentExtensionTest_LocalisedAnother';

private static $field_exclude = [
'Record', // overrides data_include varchar
];

private static $field_include = [
'Data' // overrides data_exclude varchar(100)
];

private static $db = [
'Record' => 'Varchar',
'Bastion' => 'Varchar',
'Data' => 'Varchar(100)',
'Cycle' => 'Varchar(100)',
];
}
19 changes: 19 additions & 0 deletions tests/php/Extension/FluentExtensionTest/LocalisedChild.php
@@ -0,0 +1,19 @@
<?php

namespace TractorCow\Fluent\Tests\Extension\FluentExtensionTest;

use SilverStripe\Dev\TestOnly;

class LocalisedChild extends LocalisedParent implements TestOnly
{
private static $table_name = 'FluentExtensionTest_LocalisedChild';

private static $translate = [
'Record',
];

private static $db = [
'Record' => 'Text',
'Data' => 'Text',
];
}
33 changes: 33 additions & 0 deletions tests/php/Extension/FluentExtensionTest/LocalisedParent.php
@@ -0,0 +1,33 @@
<?php

namespace TractorCow\Fluent\Tests\Extension\FluentExtensionTest;

use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\DataObject;
use TractorCow\Fluent\Extension\FluentExtension;

/**
* @mixin FluentExtension
*/
class LocalisedParent extends DataObject implements TestOnly
{
private static $table_name = 'FluentExtensionTest_LocalisedParent';

private static $extensions = [
'FluentExtension' => FluentExtension::class,
];

private static $data_include = [
'Varchar',
];

private static $data_exclude = [
'Varchar(100)',
];

private static $db = [
'Title' => 'Varchar',
'Description' => 'Varchar(100)',
'Details' => 'Varchar(200)',
];
}
17 changes: 17 additions & 0 deletions tests/php/Extension/FluentExtensionTest/UnlocalisedChild.php
@@ -0,0 +1,17 @@
<?php

namespace TractorCow\Fluent\Tests\Extension\FluentExtensionTest;

use SilverStripe\Dev\TestOnly;

class UnlocalisedChild extends LocalisedParent implements TestOnly
{
private static $table_name = 'FluentExtensionTest_UnlocalisedChild';

private static $translate = 'none';

private static $db = [
'Record' => 'Text',
'Data' => 'Text',
];
}

0 comments on commit 597319b

Please sign in to comment.