Skip to content

Commit

Permalink
Merge branch '1.x' into fix/blank-response-header
Browse files Browse the repository at this point in the history
  • Loading branch information
bryannielsen committed Nov 17, 2023
2 parents f112ba8 + 4f84b71 commit 360e815
Show file tree
Hide file tree
Showing 20 changed files with 95 additions and 55 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
### Added

- Artisan command to proxy ExpressionEngine's `eecli` commands in the context of Laravel and Coilpack after they have fully booted. This command is available through `php artisan eecli`.
- Parameter to Channel Entries Tag for eager loading relationships. This allows for easier inclusion of custom field data with `exp.channel.entries({with: 'data'})`
### Fixed

- Behavior of File Modifier to return original data when manipulation fails silently
- Generic fieldtype passes `content_id` along to core fieldtype handler and triggers `pre_process` hook.
- Error checking and handling for GraphQL compatible fieldtypes
- Trigger `core_boot` hook during GraphQL requests
- Handling of blank headers in HTTP responses
- Custom field orderby clauses when it uses legacy field data storage

## [1.1.2] - 2023-07-27

Expand Down
6 changes: 3 additions & 3 deletions src/Api/Graph/Support/FieldtypeRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,15 +107,15 @@ public function registerField(ChannelField $field)
return $this->types[$field->field_name];
}

if (!$fieldtype instanceof GeneratesGraphType) {
if (! $fieldtype instanceof GeneratesGraphType) {
return null;
}

$name = "Field__{$field->field_name}";
$typeDefinition = $fieldtype->generateGraphType($field);

if(!$typeDefinition instanceof GraphQLType) {
throw new \Exception("Generated GraphQL Type for field {$field->field_name} must extend ".GraphQLType::class.".");
if (! $typeDefinition instanceof GraphQLType) {
throw new \Exception("Generated GraphQL Type for field {$field->field_name} must extend ".GraphQLType::class.'.');
}

$typeDefinition->name = $name;
Expand Down
2 changes: 1 addition & 1 deletion src/Bootstrap/LoadExpressionEngine.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public function cli()
// fake SERVER vars for CLI context
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->constants['REQ'] = 'CLI';
$this->constants['EE_INSTALLED'] = file_exists($this->constants['SYSPATH'] . 'user/config/config.php');
$this->constants['EE_INSTALLED'] = file_exists($this->constants['SYSPATH'].'user/config/config.php');

return $this;
}
Expand Down
24 changes: 11 additions & 13 deletions src/Commands/EECliCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@

class EECliCommand extends Command
{

use CanAccessRestrictedClass;

public $signature = 'eecli {args?*}';

public $description = "Run ExpressionEngine commands through Coilpack";
public $description = 'Run ExpressionEngine commands through Coilpack';

private $cli;

public function __construct()
{
parent::__construct();

if(($_SERVER['argv'][1] ?? '') != 'eecli') {
if (($_SERVER['argv'][1] ?? '') != 'eecli') {
return;
}

Expand All @@ -29,10 +28,10 @@ public function __construct()

$this->setupCommand($_SERVER['argv'][2] ?? 'list');

foreach($_SERVER['argv'] as $arg) {
foreach ($_SERVER['argv'] as $arg) {
// If we have a help flag we need to get out early so that it can be
// handled by the ExpressionEngine Command and not by Symfony/Laravel
if($arg == '--help') {
if ($arg == '--help') {
return $this->handle();
}
}
Expand All @@ -42,7 +41,7 @@ protected function setupCommand($command)
{
$availableCommands = $this->callRestrictedMethod($this->cli, 'availableCommands');

if (!array_key_exists($command, $availableCommands)) {
if (! array_key_exists($command, $availableCommands)) {
exit("Command '$command' not found.");
}

Expand All @@ -61,23 +60,22 @@ protected function setupCommand($command)
$this->callRestrictedMethod($commandClass, 'loadOptions');
// Convert ExpressionEngine Command's option syntax into Laravel's and update the signature
$options = $this->getRestrictedProperty($commandClass, 'commandOptions');
$this->signature = $this->signature . ' ' . implode(' ', array_map(function($option) {
if(strpos($option, 'verbose') !== false) {
$this->signature = $this->signature.' '.implode(' ', array_map(function ($option) {
if (strpos($option, 'verbose') !== false) {
return '';
}

$pieces = explode(',', $option);
if(count($pieces) == 2) {
$option = substr($pieces[1], 0, 1) . '|' . $pieces[0];
if (count($pieces) == 2) {
$option = substr($pieces[1], 0, 1).'|'.$pieces[0];
}
return "{--".$option."=}";

return '{--'.$option.'=}';
}, array_keys($options)));
}


public function handle(): int
{
return $this->cli->process();
}

}
1 change: 0 additions & 1 deletion src/Core.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

class Core
{

use Traits\CanAccessRestrictedClass;

protected $core;
Expand Down
2 changes: 1 addition & 1 deletion src/Fieldtypes/Generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public function apply(FieldContent $content, array $parameters = [])
$data = $content->getAttribute('data');

// Run pre_process if it exists
if(method_exists($handler, 'pre_process')) {
if (method_exists($handler, 'pre_process')) {
$data = $handler->pre_process($data);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Fieldtypes/Modifiers/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ public function handle(FieldtypeOutput $content, $parameters = [])
$modified = (is_array($modified) && count($modified) === 1 && is_array(current($modified))) ? $modified[0] : $modified;

// If a manipulation fails and returns a boolean we fallback to the original url
if(is_bool($modified)) {
if (is_bool($modified)) {
$modified = [
'url' => $data['url'] ?? ''
'url' => $data['url'] ?? '',
];
}

Expand Down
2 changes: 1 addition & 1 deletion src/Models/Addon/Fluid/Data.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public function scopeCustomFields($query, $fields = null)

// Get a set of table names for fields that do not store data on the legacy table
$fields = $fields->filter(function ($field) {
return $field->legacy_field_data === 'n' || $field->legacy_field_data === false || $field->hasDataTable();
return ! $field->hasLegacyFieldData() || $field->hasDataTable();
});

// Join these extra field data tables
Expand Down
25 changes: 14 additions & 11 deletions src/Models/Category/Category.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Expressionengine\Coilpack\FieldtypeManager;
use Expressionengine\Coilpack\Model;
use Expressionengine\Coilpack\Models\Channel\ChannelData;
use Expressionengine\Coilpack\Models\Channel\ChannelEntry;
use Expressionengine\Coilpack\Models\FieldContent;

Expand Down Expand Up @@ -46,28 +47,30 @@ public function scopeOrderByCustomField($query, $field, $direction = 'asc')
$field = $manager->getField($field, 'category');
$column = "field_id_{$field->field_id}";

// If this field is not storing it's data on the category_field_data table we
// will join the separate data table with a unique orderby_field_name alias
if ($field->legacy_field_data === 'n' || $field->legacy_field_data === false) {
$alias = "orderby_{$field->field_name}";
$column = "$alias.$column";
$this->scopeJoinFieldDataTable($query, $field, $alias);
}
// Setup our join for the appropriate channel data table and alias
$alias = $field->hasLegacyFieldData() ? 'orderby_legacy_data' : "orderby_{$field->field_name}";
$this->scopeJoinFieldDataTable($query, $field, $alias);
$query->select($this->qualifyColumn('*'));

return $query->orderBy($column, $direction);
return $query->orderBy("$alias.$column", $direction);
}

public function scopeJoinFieldDataTable($query, $field, $alias = null)
{
if ($field->legacy_field_data == 'y' || $field->legacy_field_data === true) {
// If we have already joined this table alias just do nothing
$alreadyJoined = collect($query->getQuery()->joins)->pluck('table')->contains(function ($joinTable) use ($alias) {
return strpos($joinTable, $alias) !== false;
});

if ($alreadyJoined) {
return $query;
}

$table = $field->data_table_name;
$table = $field->hasLegacyFieldData() ? (new ChannelData)->getTable() : $field->data_table_name;
$joinTable = $alias ? "$table as $alias" : $table;
$alias = $alias ?: $table;
$query->leftJoin($joinTable, "$alias.cat_id", '=', $this->qualifyColumn('cat_id'));
$query->select('*', $this->qualifyColumn('cat_id'));
$query->select($this->qualifyColumn('*'));

return $query;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Models/Category/CategoryData.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function scopeCustomFields($query, $fields = null)

// Get a set of table names for fields that do not store data on the legacy table
$fieldtypeTables = $fields->filter(function ($field) {
return $field->legacy_field_data === 'n' || $field->legacy_field_data === false;
return ! $field->hasLegacyFieldData();
})->map(function ($field) {
return $field->data_table_name;
});
Expand Down
2 changes: 1 addition & 1 deletion src/Models/Channel/ChannelData.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function scopeCustomFields($query, $fields = null)

// Get a set of table names for fields that do not store data on the legacy table
$fieldtypeTables = $fields->filter(function ($field) {
return $field->legacy_field_data === 'n' || $field->legacy_field_data === false;
return ! $field->hasLegacyFieldData();
})->map(function ($field) {
return $field->data_table_name;
});
Expand Down
24 changes: 13 additions & 11 deletions src/Models/Channel/ChannelEntry.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,28 +132,30 @@ public function scopeOrderByCustomField($query, $field, $direction = 'asc')
$field = $manager->getField($field);
$column = "field_id_{$field->field_id}";

// If this field is not storing it's data on the channel_data table we
// will join the separate data table with a unique orderby_field_name alias
if ($field->legacy_field_data === 'n' || $field->legacy_field_data === false) {
$alias = "orderby_{$field->field_name}";
$column = "$alias.$column";
$this->scopeJoinFieldDataTable($query, $field, $alias);
}
// Setup our join for the appropriate channel data table and alias
$alias = $field->hasLegacyFieldData() ? 'orderby_legacy_data' : "orderby_{$field->field_name}";
$this->scopeJoinFieldDataTable($query, $field, $alias);
$query->select($this->qualifyColumn('*'));

return $query->orderBy($column, $direction);
return $query->orderBy("$alias.$column", $direction);
}

public function scopeJoinFieldDataTable($query, $field, $alias = null)
{
if ($field->legacy_field_data == 'y' || $field->legacy_field_data === true) {
// If we have already joined this table alias just do nothing
$alreadyJoined = collect($query->getQuery()->joins)->pluck('table')->contains(function ($joinTable) use ($alias) {
return strpos($joinTable, $alias) !== false;
});

if ($alreadyJoined) {
return $query;
}

$table = $field->data_table_name;
$table = $field->hasLegacyFieldData() ? (new ChannelData)->getTable() : $field->data_table_name;
$joinTable = $alias ? "$table as $alias" : $table;
$alias = $alias ?: $table;
$query->leftJoin($joinTable, "$alias.entry_id", '=', $this->qualifyColumn('entry_id'));
$query->select('*', $this->qualifyColumn('entry_id'));
$query->select($this->qualifyColumn('*'));

return $query;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Models/Channel/ChannelField.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,4 +75,9 @@ public function getGraphType()
{
return $this->getFieldType()->getGraphType($this);
}

public function hasLegacyFieldData()
{
return $this->legacy_field_data === 'y' || $this->legacy_field_data === true;
}
}
2 changes: 1 addition & 1 deletion src/Models/Member/MemberData.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public function scopeCustomFields($query, $fields = null)

// Get a set of table names for fields that do not store data on the legacy table
$fieldtypeTables = $fields->filter(function ($field) {
return $field->m_legacy_field_data === false || $field->m_legacy_field_data === 'n';
return ! $field->hasLegacyFieldData();
})->map(function ($field) {
return $field->data_table_name;
});
Expand Down
5 changes: 5 additions & 0 deletions src/Models/Member/MemberField.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,9 @@ public function getDataTableNameAttribute($value)
{
return "member_data_field_{$this->m_field_id}";
}

public function hasLegacyFieldData()
{
return $this->m_legacy_field_data === 'y' || $this->m_legacy_field_data === true;
}
}
6 changes: 3 additions & 3 deletions src/Support/Arguments/FilterArgument.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ public function __construct($value)
protected function parse($value)
{
// Check for a negated set
if (strncasecmp($value, 'not ', 4) == 0) {
if (strncasecmp($value ?? '', 'not ', 4) == 0) {
$this->not = true;
$value = substr($value, 4);
}

// Check for set joined by logical and
if (strpos($value, '&&')) {
if (strpos($value ?? '', '&&')) {
$this->separator = '&&';
$this->boolean = 'and';
}
Expand All @@ -44,7 +44,7 @@ protected function parse($value)
$this->boolean = ($this->boolean === 'or') ? 'and' : 'or';
}

$terms = array_filter(explode($this->separator, $value), function ($term) {
$terms = array_filter(explode($this->separator, $value ?? ''), function ($term) {
return ! is_null($term) && trim($term) !== '';
});

Expand Down
7 changes: 3 additions & 4 deletions src/Traits/CanAccessRestrictedClass.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Expressionengine\Coilpack\Traits;

trait CanAccessRestrictedClass {

trait CanAccessRestrictedClass
{
protected function getRestrictedProperty($object, $property)
{
$reflection = new \ReflectionClass($object);
Expand All @@ -28,5 +28,4 @@ public function callRestrictedMethod($object, $method, $parameters = [])

return $method->invoke($object, ...$parameters);
}

}
}
2 changes: 1 addition & 1 deletion src/Traits/DataAcrossTables.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function scopeCustomFields($query, $fields = null)

// Get a set of table names for fields that do not store data on the legacy table
$fieldtypeTables = $fields->filter(function ($field) {
return $field->legacy_field_data === 'n' || $field->legacy_field_data === false;
return ! $field->hasLegacyFieldData();
})->map(function ($field) {
return $field->data_table_name;
});
Expand Down
16 changes: 16 additions & 0 deletions src/View/ModelTag.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Expressionengine\Coilpack\View;

use Expressionengine\Coilpack\Support\Arguments\ListArgument;
use Expressionengine\Coilpack\Support\Parameter;

abstract class ModelTag extends IterableTag
Expand Down Expand Up @@ -35,9 +36,20 @@ public function defineParameters(): array
'description' => 'How many results to show on each page',
'defaultValue' => 10,
]),
new Parameter([
'name' => 'with',
'type' => 'string',
'description' => 'A pipe separated list of relationships to eager load',
'defaultValue' => null,
]),
];
}

public function getWithArgument($value)
{
return new ListArgument($value);
}

public function run()
{
if ($this->hasArgument('page') || $this->hasArgument('per_page')) {
Expand All @@ -57,6 +69,10 @@ public function run()
$this->query->take($this->getArgument('limit')->value);
}

if ($this->hasArgument('with')) {
$this->query->with($this->getArgument('with')->terms->map->value->toArray());
}

return $this->query->get();
}

Expand Down
Loading

0 comments on commit 360e815

Please sign in to comment.