I'm trying to create a web API in Laravel.
I'm using the following packages for me to manage the REST return: fractal, spatie/fractal and also ApiGuard.
My controller has the following code:
<?php
namespace App\Http\Controllers;
use Chrisbjr\ApiGuard\Http\Controllers\ApiGuardController;
use App\CSV;
use App\CSVTransformer;
class ApiController extends ApiGuardController
{
public function info()
{
$csvs = CSV::all();
$estado = [0,0,0,0];
foreach ($csvs as $csv) {
switch ($csv->estado) {
case "Renovado":
$estado[0]++;
break;
case "Expirado":
$estado[1]++;
break;
case "Aguardar Pagamento":
$estado[2]++;
break;
case "Não Renovado":
$estado[3]++;
break;
}
}
return $this->response->withCollection($csvs, new CSVTransformer);
}
public function renovacoes() {
$csvs = CSV::all();
return json_encode([ "data" => $csvs ]);
}
}
This is what the transformer looks like:
namespace App;
use App\CSV;
use League\Fractal\TransformerAbstract;
class CSVTransformer extends TransformerAbstract
{
public function transform(CSV $csv)
{
return [
'id' => (int) $csv->id,
'renovacao' => $csv->renovacao
];
}
}
The problem is, when accessing the chosen POST route to get the JSON return, the following error is thrown:
Class 'League\Fractal\TransformerAbstract' not found.
How do I solve this, so that my transformer works as it is supposed to?
EDIT:
Also, here's the CSV class:
<?php
namespace App;
use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
class CSV extends Model
{
protected $table = "csv";
}
Routes file:
Route::group(["middleware" => ["apiguard"]], function () {
Route::group(['prefix' => 'api'], function () {
Route::group(['prefix' => 'v1'], function () {
Route::post("/renovations/info","ApiController#info");
Route::post("/renovations","ApiController#renovacoes");
});
});
});
Your vendor/league folder should look like this:
fractal
├── LICENSE
├── composer.json
└── src
├── Manager.php
├── Pagination
├── ParamBag.php
├── Resource
├── Scope.php
├── Serializer
└── TransformerAbstract.php
There is a TransformerAbstract.php with this content:
<?php
/*
* This file is part of the League\Fractal package.
*
* (c) Phil Sturgeon <me#philsturgeon.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace League\Fractal;
use League\Fractal\Resource\Collection;
use League\Fractal\Resource\Item;
use League\Fractal\Resource\NullResource;
use League\Fractal\Resource\ResourceAbstract;
/**
* Transformer Abstract
*
* All Transformer classes should extend this to utilize the convenience methods
* collection() and item(), and make the self::$availableIncludes property available.
* Extend it and add a `transform()` method to transform any default or included data
* into a basic array.
*/
abstract class TransformerAbstract
{
/**
* Resources that can be included if requested.
*
* #var array
*/
protected $availableIncludes = [];
/**
* Include resources without needing it to be requested.
*
* #var array
*/
protected $defaultIncludes = [];
/**
* The transformer should know about the current scope, so we can fetch relevant params.
*
* #var Scope
*/
protected $currentScope;
/**
* Getter for availableIncludes.
*
* #return array
*/
public function getAvailableIncludes()
{
return $this->availableIncludes;
}
/**
* Getter for defaultIncludes.
*
* #return array
*/
public function getDefaultIncludes()
{
return $this->defaultIncludes;
}
/**
* Getter for currentScope.
*
* #return \League\Fractal\Scope
*/
public function getCurrentScope()
{
return $this->currentScope;
}
/**
* Figure out which includes we need.
*
* #internal
*
* #param Scope $scope
*
* #return array
*/
private function figureOutWhichIncludes(Scope $scope)
{
$includes = $this->getDefaultIncludes();
foreach ($this->getAvailableIncludes() as $include) {
if ($scope->isRequested($include)) {
$includes[] = $include;
}
}
return $includes;
}
/**
* This method is fired to loop through available includes, see if any of
* them are requested and permitted for this scope.
*
* #internal
*
* #param Scope $scope
* #param mixed $data
*
* #return array
*/
public function processIncludedResources(Scope $scope, $data)
{
$includedData = [];
$includes = $this->figureOutWhichIncludes($scope);
foreach ($includes as $include) {
$includedData = $this->includeResourceIfAvailable(
$scope,
$data,
$includedData,
$include
);
}
return $includedData === [] ? false : $includedData;
}
/**
* Include a resource only if it is available on the method.
*
* #internal
*
* #param Scope $scope
* #param mixed $data
* #param array $includedData
* #param string $include
*
* #return array
*/
private function includeResourceIfAvailable(
Scope $scope,
$data,
$includedData,
$include
) {
if ($resource = $this->callIncludeMethod($scope, $include, $data)) {
$childScope = $scope->embedChildScope($include, $resource);
$includedData[$include] = $childScope->toArray();
}
return $includedData;
}
/**
* Call Include Method.
*
* #internal
*
* #param Scope $scope
* #param string $includeName
* #param mixed $data
*
* #throws \Exception
*
* #return \League\Fractal\Resource\ResourceInterface
*/
protected function callIncludeMethod(Scope $scope, $includeName, $data)
{
$scopeIdentifier = $scope->getIdentifier($includeName);
$params = $scope->getManager()->getIncludeParams($scopeIdentifier);
// Check if the method name actually exists
$methodName = 'include'.str_replace(' ', '', ucwords(str_replace('_', ' ', str_replace('-', ' ', $includeName))));
$resource = call_user_func([$this, $methodName], $data, $params);
if ($resource === null) {
return false;
}
if (! $resource instanceof ResourceAbstract) {
throw new \Exception(sprintf(
'Invalid return value from %s::%s(). Expected %s, received %s.',
__CLASS__,
$methodName,
'League\Fractal\Resource\ResourceAbstract',
gettype($resource)
));
}
return $resource;
}
/**
* Setter for availableIncludes.
*
* #param array $availableIncludes
*
* #return $this
*/
public function setAvailableIncludes($availableIncludes)
{
$this->availableIncludes = $availableIncludes;
return $this;
}
/**
* Setter for defaultIncludes.
*
* #param array $defaultIncludes
*
* #return $this
*/
public function setDefaultIncludes($defaultIncludes)
{
$this->defaultIncludes = $defaultIncludes;
return $this;
}
/**
* Setter for currentScope.
*
* #param Scope $currentScope
*
* #return $this
*/
public function setCurrentScope($currentScope)
{
$this->currentScope = $currentScope;
return $this;
}
/**
* Create a new item resource object.
*
* #param mixed $data
* #param TransformerAbstract|callable $transformer
* #param string $resourceKey
*
* #return Item
*/
protected function item($data, $transformer, $resourceKey = null)
{
return new Item($data, $transformer, $resourceKey);
}
/**
* Create a new collection resource object.
*
* #param mixed $data
* #param TransformerAbstract|callable $transformer
* #param string $resourceKey
*
* #return Collection
*/
protected function collection($data, $transformer, $resourceKey = null)
{
return new Collection($data, $transformer, $resourceKey);
}
/**
* Create a new null resource object.
*
* #return NullResource
*/
protected function null()
{
return new NullResource();
}
}
Maybe you should reinstall the fractal package.
Reinstall the package:
Remove the old one composer remove league/fractal
Optional: Update composer with the latest package versions composer update
Install fractal composer require league/fractal
Related
Im using this library: https://www.laravelplay.com/packages/ycs77::laravel-wizard
I did all steps and have the same result like in the example.
Im trying to get data from database to each step.
Model (App/steps/intro/DropboxStep.php):
<?php
namespace App\Steps\Intro;
use Illuminate\Http\Request;
use Ycs77\LaravelWizard\Step;
use DB;
class DropboxStep extends Step
{
/**
* The step slug.
*
* #var string
*/
protected $slug = 'dropbox';
/**
* The step show label text.
*
* #var string
*/
protected $label = 'Dropbox';
/**
* The step form view path.
*
* #var string
*/
protected $view = 'steps.intro.dropbox';
/**
* Set the step model instance or the relationships instance.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation|null
*/
public function model(Request $request)
{
//
}
/**
* Save this step form data.
*
* #param \Illuminate\Http\Request $request
* #param array|null $data
* #param \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Relations\Relation|null $model
* #return void
*/
public function saveData(Request $request, $data = null, $model = null)
{
//
}
/**
* Validation rules.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function rules(Request $request)
{
return [];
}
public function getOptions()
{
$stepa2 = DB::table('tutorials')->where('id', '2')->first();
return [
'stepa2' => $stepa2,
'Lucas',
];
}
}
View:
<div class="form-group">
{{ $stepa2 }}
</div>
Result:
Undefined variable: stepa2
Tried also through controller (IntroWizardController.php)
This is default controller:
<?php
namespace App\Http\Controllers;
use App\Steps\Intro\DropboxStep;
use App\Steps\Intro\H2NStep;
use App\Steps\Intro\PT4Step;
use Ycs77\LaravelWizard\Wizardable;
use DB;
class IntroWizardController extends Controller
{
use Wizardable;
/**
* The wizard name.
*
* #var string
*/
protected $wizardName = 'intro';
/**
* The wizard title.
*
* #var string
*/
protected $wizardTitle = 'Intro';
/**
* The wizard options.
*
* #var array
*/
protected $wizardOptions = [];
/**
* The wizard steps instance.
*
* #var array
*/
protected $steps = [
DropboxStep::class,
H2NStep::class,
PT4Step::class,
];
}
I added:
public function getOptions()
{
$stepa2 = DB::table('tutorials')->where('id', '2')->first();
return [
'stepa2' => $stepa2,
'Lucas',
];
}
Tried also in controller return to view, but then I get result from database in blank page, not with other parts.
Is it possible to pass database query to view with this library?
Thanks
EDIT:
With this routes:
Route::get('wizard/intro/dropbox', 'IntroWizardController#step1a', 'wizard.intro.dropbox');
Wizard::routes('wizard/intro', 'IntroWizardController', 'wizard.intro');
I get my result from database, but like I said before in white blank page:
But I want to get in this view like others (without query):
To pass any data from controller to blade view simply use these 2 options
option 1:
public function someFunction()
{
$model = DB::table('model_table')->where('id', '2')->first();
return view('blade_view_name', compact('model'));
}
option 2:
public function someFunction()
{
$model = DB::table('model_table')->where('id', '2')->first();
return view('blade_view_name')->with('model', $model);
}
if you have more or want more of variables you can chain the with() method like this:
return view('blade_view_name')
->with('model', $model)
->with('variable', 'Some other variable');
I recently found this laravel package on github and after installing it, I noticed that the setLocale() function in Translation.php class didn't work as expected. Many issues have been opened about this bug and I started trying to fix it as the maintainer's time was limited due to other open source projects : (Adldap2/Adldap2 and Adldap2/Adldap2-Laravel as stated here.
In Translation.php class, I first changed getLocale() function from
public function getLocale()
{
if ($this->request->hasCookie('locale')) {
return $this->request->cookie('locale');
} else {
return $this->getConfigDefaultLocale();
}
}
to
public function getLocale()
{
if ($this->request->session()->has('locale')) {
return $this->request->session()->get('locale');
} else {
return $this->getConfigDefaultLocale();
}
}
because the comments in the Translation contract specified that sessions were to be used for the getLocale() function.
Then, after inserting some text to translate with the provided helper function {{_t('text')}} in my blade templates, I dumped the result in my localization middleware to see if everything was fine with dd(Translate::getLocale).
I got a 'Session store not set on request.' error. After some googling, I found this thread on Stack Overflow and decided to use the helper function session() and later the Session facade as I found it more convenient.
After these operations, dd(Translate::getLocale) worked fine and I proceeded to modify setLocale() function from
public function setLocale($code = '')
{
$this->locale = $code;
}
to
public function setLocale($code = '')
{
$this->locale = $code;
$this->request->session()->put('locale', $locale);
}
and lastly
public function setLocale($code = '')
{
$this->locale = $code;
session(['locale' => $this->locale]);
}
in order to avoid the 'Session store not set on request' error.
Everything worked correctly.
Despite these actions, the translation still didn't work as expected.
Then, I dumped Translation::getLocale() first in my localization middleware and then in my Translation.php class and I got two different results : 'en' for the Translation.php class and 'fr'(Automatically detected by the middleware).
I also added automatic locale detection to the Translation following this code snippet and tried again with no success.
Finally, I dumped the session variable with dd(Session::all) in the two files and got this from Translation.php class :
array:4 [▼
"locale" => "en"
"_token" => "3FYWDEaPk47ZcqIS2u5KzGmwz7vI0x3G8h9GkNZP"
"_previous" => array:1 [▶]
"_flash" => array:2 [▶]
]
and that from localization middleware :
array:4 [▼
"locale" => "fr"
]
After checking my database where my sessions are stored, I found only one session, so I don't understand why this is happening.
I also checked some blog posts about laravel service container as $app->make() have been used in Translation class constructor, but I'm stuck.
Here are my current Translation.php and localization middleware :
<?php
namespace App\Http\Middleware;
use Closure;
use Carbon\Carbon;
use Session;
use App;
use Config;
use Translation;
use Illuminate\Http\Request;
class Localization
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ( Session::has('locale')) {
$locale = Session::get('locale', Config::get('app.locale'));
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
/*$this->config->get('translation.locales')*/
if ($locale != 'fr' && $locale != 'en') {
$locale = 'en';
}
Session::put('locale', $locale);
}
App::setLocale($locale);
//dd(Session::get('locale'));
Translation::setLocale($locale);
return $next($request);
}
}
<?php
namespace Stevebauman\Translation;
use ErrorException;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\App;
use InvalidArgumentException;
use Stevebauman\Translation\Contracts\Client as ClientInterface;
use Stevebauman\Translation\Contracts\Translation as TranslationInterface;
use UnexpectedValueException;
//use Illuminate\Http\Request;
class Translation implements TranslationInterface
{
/**
* Holds the application .
*
* #var object
*/
protected $app ;
/**
* Holds the application locale.
*
* #var string
*/
protected $locale = '';
/**
* Holds the locale model.
*
* #var Model
*/
protected $localeModel;
/**
* Holds the translation model.
*
* #var Model
*/
protected $translationModel;
/**
* Holds the translation client.
*
* #var ClientInterface
*/
protected $client;
/**
* Holds the current cache instance.
*
* #var \Illuminate\Cache\CacheManager
*/
protected $cache;
/**
* Holds the current config instance.
*
* #var \Illuminate\Config\Repository
*/
protected $config;
/**
* Holds the current request instance.
*
* #var \Illuminate\Http\Request
*/
protected $request;
/**
* The default amount of time (minutes) to store the cached translations.
*
* #var int
*/
private $cacheTime = 30;
/**
* {#inheritdoc}
*/
public function __construct(Application $app)
{
$this->app = $app;
$this->config = $this->app->make('config');
$this->cache = $this->app->make('cache');
$this->request = $this->app->make('request');
$this->localeModel = $this->app->make($this->getConfigLocaleModel());
$this->translationModel = $this->app->make($this->getConfigTranslationModel());
$this->client = $this->app->make($this->getConfigClient());
// Set the default locale to the current application locale
$this->setLocale($this->getConfigDefaultLocale());
// Set the cache time from the configuration
$this->setCacheTime($this->getConfigCacheTime());
}
/**
* {#inheritdoc}
*/
public function translate($text = '', $replacements = [], $toLocale = '', $runOnce = false)
{
try {
// Make sure $text is actually a string and not and object / int
$this->validateText($text);
// Get the default translation text. This will insert the translation
// and the default application locale if they don't
// exist using firstOrCreate
$defaultTranslation = $this->getDefaultTranslation($text);
// If there are replacements inside the array we need to convert them
// into google translate safe placeholders. ex :name to __name__
if (count($replacements) > 0) {
$defaultTranslation->translation = $this->makeTranslationSafePlaceholders($text, $replacements);
}
if ( Session::has('locale')) {
$locale = Session::get('locale');
} else {
$locale = substr($request->server('HTTP_ACCEPT_LANGUAGE'), 0, 2);
/*$this->config->get('translation.locales')*/
if ($locale != 'fr' && $locale != 'en') {
$locale = 'en';
}
Session::put('locale', $locale);
}
App::setLocale($locale);
//dd($locale);
Translation::setLocale($locale);
// If a toLocale has been provided, we're only translating a single string, so
// we won't call the getLocale method as it retrieves and sets the default
// session locale. If it has not been provided, we'll get the
// default locale, and set it on the current session.
if ($toLocale) {
$toLocale = $this->firstOrCreateLocale($toLocale);
} else {
//dd(Session::all());
$toLocale = $this->firstOrCreateLocale($this->getLocale());
}
// Check if translation is requested for default locale.
// If it is default locale we can just return default translation.
if ($defaultTranslation->getAttribute($this->localeModel->getForeignKey()) == $toLocale->getKey()) {
return $this->makeReplacements($defaultTranslation->translation, $replacements);
}
// Since we are not on default translation locale, we will have to
// create (or get first) translation for provided locale where
// parent translation is our default translation.
$translation = $this->firstOrCreateTranslation(
$toLocale,
$defaultTranslation->translation,
$defaultTranslation
);
//dd($this->makeReplacements($translation->translation, $replacements));
return $this->makeReplacements($translation->translation, $replacements);
} catch (\Illuminate\Database\QueryException $e) {
// If foreign key integrity constrains fail, we have a caching issue
if (!$runOnce) {
// If this has not been run before, proceed
// Burst locale cache
$this->removeCacheLocale($toLocale->code);
// Burst translation cache
$this->removeCacheTranslation($this->translationModel->firstOrNew([
$toLocale->getForeignKey() => $toLocale->getKey(),
'translation' => $text,
])
);
// Attempt translation 1 more time
return $this->translate($text, $replacements, $toLocale->code, $runOnce = true);
} else {
// If it has already tried translating once and failed again,
// prevent infinite loops and just return the text
return $text;
}
}
}
/**
* {#inheritdoc}
*/
public function getAppLocale()
{
return $this->config->get('app.locale');
}
/**
* {#inheritdoc}
*/
public function getRoutePrefix()
{
$locale = $this->request->segment($this->getConfigRequestSegment());
$locales = $this->getConfigLocales();
if (is_array($locales) && in_array($locale, array_keys($locales))) {
return $locale;
}
}
/**
* {#inheritdoc}
*/
public function getLocale()
{
//dd($this->request->session()->has('locale'));
/*if ($this->request->session()->has('locale')) {
return $this->request->session()->get('locale');
} else {
return $this->getConfigDefaultLocale();
}*/
/*if (session('locale')) {
return session('locale');
} else {
return $this->getConfigDefaultLocale();
}*/
return Session::get('locale'); //$this->locale;
}
/**
* {#inheritdoc}
*/
public function setLocale($code = '')
{
$this->locale = $code;
/*if(in_array($code, $this->config->get('translation.locales'))){*/
//$this->request->session()->put('locale', $locale);
session(['locale' => $this->locale]);
/* } else {} */
}
/**
* {#inheritdoc}
*/
public function getDefaultTranslation($text)
{
$locale = $this->firstOrCreateLocale($this->getConfigDefaultLocale());
return $this->firstOrCreateTranslation($locale, $text);
}
/**
* Replaces laravel translation placeholders with google
* translate safe placeholders. Ex:.
*
* Converts:
* :name
*
* Into:
* __name__
*
* #param string $text
* #param array $replace
*
* #return mixed
*/
protected function makeTranslationSafePlaceholders($text, array $replace = [])
{
if (count($replace) > 0) {
foreach ($replace as $key => $value) {
// Search for :key
$search = ':'.$key;
// Replace it with __key__
$replace = $this->makeTranslationSafePlaceholder($key);
// Perform the replacements
$text = str_replace($search, $replace, $text);
}
}
return $text;
}
/**
* Makes a placeholder by the specified key.
*
* #param string $key
*
* #return string
*/
protected function makeTranslationSafePlaceholder($key = '')
{
return '___'.strtolower($key).'___';
}
/**
* Make the place-holder replacements on the specified text.
*
* #param string $text
* #param array $replacements
*
* #return string
*/
protected function makeReplacements($text, array $replacements)
{
if (count($replacements) > 0) {
foreach ($replacements as $key => $value) {
$replace = $this->makeTranslationSafePlaceholder($key);
$text = str_ireplace($replace, $value, $text);
}
}
return $text;
}
/**
* Retrieves or creates a locale from the specified code.
*
* #param string $code
*
* #return Model
*/
protected function firstOrCreateLocale($code)
{
$cachedLocale = $this->getCacheLocale($code);
if ($cachedLocale) {
return $cachedLocale;
}
$name = $this->getConfigLocaleByCode($code);
$locale = $this->localeModel->firstOrCreate([
'code' => $code,
'name' => $name,
]);
$this->setCacheLocale($locale);
return $locale;
}
/**
* Creates a translation.
*
* #param Model $locale
* #param string $text
* #param Model $parentTranslation
*
* #return Model
*/
protected function firstOrCreateTranslation(Model $locale, $text, $parentTranslation = null)
{
// We'll check to see if there's a cached translation
// first before we try and hit the database.
$cachedTranslation = $this->getCacheTranslation($locale, $text);
if ($cachedTranslation instanceof Model) {
return $cachedTranslation;
}
// Check if auto translation is enabled. If so we'll run
// the text through google translate and
// save it, then cache it.
if ($parentTranslation && $this->autoTranslateEnabled()) {
$this->client->setSource($parentTranslation->locale->code);
$this->client->setTarget($locale->code);
try {
$text = $this->client->translate($text);
} catch (ErrorException $e) {
// Request to translate failed, set the text
// to the parent translation.
$text = $parentTranslation->translation;
} catch (UnexpectedValueException $e) {
// Looks like something other than text was passed in,
// we'll set the text to the parent translation
// for this exception as well.
$text = $parentTranslation->translation;
}
}
if ($parentTranslation) {
// If a parent translation is given we're looking for it's child translation.
$translation = $this->translationModel->firstOrNew([
$locale->getForeignKey() => $locale->getKey(),
$this->translationModel->getForeignKey() => $parentTranslation->getKey(),
]);
} else {
// Otherwise we're creating the parent translation.
$translation = $this->translationModel->firstOrNew([
$locale->getForeignKey() => $locale->getKey(),
'translation' => $text,
]);
}
if (empty($translation->getAttribute('translation'))) {
// We need to make sure we don't overwrite the translation
// if it exists already in case it was modified.
$translation->setAttribute('translation', $text);
}
if ($translation->isDirty()) {
$translation->save();
}
// Cache the translation so it's retrieved faster next time
$this->setCacheTranslation($translation);
return $translation;
}
/**
* Sets a cache key to the specified locale and text.
*
* #param Model $translation
*/
protected function setCacheTranslation(Model $translation)
{
if ($translation->parent instanceof Model) {
$id = $this->getTranslationCacheId($translation->locale, $translation->parent->translation);
} else {
$id = $this->getTranslationCacheId($translation->locale, $translation->translation);
}
if (!$this->cache->has($id)) {
$this->cache->put($id, $translation, $this->cacheTime);
}
}
/**
* Remove the translation from the cache manually.
*
* #param Model $translation
*/
protected function removeCacheTranslation(Model $translation)
{
$id = $this->getTranslationCacheId($translation->locale, $translation->translation);
if ($this->cache->has($id)) {
$this->cache->forget($id);
}
}
/**
* Retrieves the cached translation from the specified locale
* and text.
*
* #param Model $locale
* #param string $text
*
* #return bool|Model
*/
protected function getCacheTranslation(Model $locale, $text)
{
$id = $this->getTranslationCacheId($locale, $text);
$cachedTranslation = $this->cache->get($id);
if ($cachedTranslation instanceof Model) {
return $cachedTranslation;
}
// Cached translation wasn't found, let's return
// false so we know to generate one.
return false;
}
/**
* Sets a cache key to the specified locale.
*
* #param Model $locale
*/
protected function setCacheLocale(Model $locale)
{
if (!$this->cache->has($locale->code)) {
$id = sprintf('translation.%s', $locale->code);
$this->cache->put($id, $locale, $this->cacheTime);
}
}
/**
* Retrieves a cached locale from the specified locale code.
*
* #param string $code
*
* #return bool
*/
protected function getCacheLocale($code)
{
$id = sprintf('translation.%s', $code);
if ($this->cache->has($id)) {
return $this->cache->get($id);
}
return false;
}
/**
* Remove a locale from the cache.
*
* #param string $code
*/
protected function removeCacheLocale($code)
{
$id = sprintf('translation.%s', $code);
if ($this->cache->has($id)) {
$this->cache->forget($id);
}
}
/**
* Returns a unique translation code by compressing the text
* using a PHP compression function.
*
* #param Model $locale
* #param string $text
*
* #return string
*/
protected function getTranslationCacheId(Model $locale, $text)
{
$compressed = $this->compressString($text);
return sprintf('translation.%s.%s', $locale->code, $compressed);
}
/**
* Returns a the english name of the locale code entered from the config file.
*
* #param string $code
*
* #return string
*/
protected function getConfigLocaleByCode($code)
{
$locales = $this->getConfigLocales();
if (is_array($locales) && array_key_exists($code, $locales)) {
return $locales[$code];
}
return $code;
}
/**
* Sets the time to store the translations and locales in cache.
*
* #param int $time
*/
protected function setCacheTime($time)
{
if (is_numeric($time)) {
$this->cacheTime = $time;
}
}
/**
* Returns the default locale from the configuration.
*
* #return string
*/
protected function getConfigDefaultLocale()
{
return $this->config->get('translation.default_locale', 'en');
}
/**
* Returns the locale model from the configuration.
*
* #return string
*/
protected function getConfigLocaleModel()
{
return $this->config->get('translation.models.locale', Models\Locale::class);
}
/**
* Returns the translation model from the configuration.
*
* #return string
*/
protected function getConfigTranslationModel()
{
return $this->config->get('translation.models.translation', Models\Translation::class);
}
/**
* Returns the translation client from the configuration.
*
* #return string
*/
protected function getConfigClient()
{
return $this->config->get('translation.clients.client', Clients\GoogleTranslate::class);
}
/**
* Returns the request segment to retrieve the locale from.
*
* #return int
*/
protected function getConfigRequestSegment()
{
return $this->config->get('translation.request_segment', 1);
}
/**
* Returns the array of configuration locales.
*
* #return array
*/
protected function getConfigLocales()
{
return $this->config->get('translation.locales');
}
/**
* Returns the cache time set from the configuration file.
*
* #return string|int
*/
protected function getConfigCacheTime()
{
return $this->config->get('translation.cache_time', $this->cacheTime);
}
/**
* Returns the auto translate configuration option.
*
* #return bool
*/
protected function autoTranslateEnabled()
{
return $this->config->get('translation.auto_translate', true);
}
/**
* Calculates the md5 hash of a string.
*
* Used for storing cache keys for translations.
*
* #param $string
*
* #return string
*/
protected function compressString($string)
{
return md5($string);
}
/**
* Validates the inserted text to make sure it's a string.
*
* #param $text
*
* #throws InvalidArgumentException
*
* #return bool
*/
protected function validateText($text)
{
if (!is_string($text)) {
$message = 'Invalid Argument. You must supply a string to be translated.';
throw new InvalidArgumentException($message);
}
return true;
}
}
Thank you!!
I'm trying to "use" a vendor script to connect to feefo api (an online reviews service) but when I try and use the script it gives me this error:
Type error: Argument 1 passed to
BlueBayTravel\Feefo\Feefo::__construct() must be an instance of
GuzzleHttp\Client, null given, called in/Users/webuser1/Projects/_websites/domain.co.uk/plugins/gavinfoster/feefo/components/Feedback.php on line 47
Here is the vendor code I'm using:
/*
* This file is part of Feefo.
*
* (c) Blue Bay Travel <developers#bluebaytravel.co.uk>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace BlueBayTravel\Feefo;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
/**
* This is the feefo class.
*
* #author James Brooks <james#bluebaytravel.co.uk>
*/
class Feefo implements Arrayable, ArrayAccess, Countable
{
/**
* The guzzle client.
*
* #var \GuzzleHttp\Client
*/
protected $client;
/**
* The config repository.
*
* #var \Illuminate\Contracts\Config\Repository
*/
protected $config;
/**
* The review items.
*
* #var array
*/
protected $data;
/**
* Create a new feefo instance.
*
* #param \GuzzleHttp\Client $client
* #param \Illuminate\Contracts\Config\Repository $config
*
* #return void
*/
public function __construct(Client $client, Repository $config)
{
$this->client = $client;
$this->config = $config;
}
/**
* Fetch feedback.
*
* #param array|null $params
*
* #return \BlueBayTravel\Feefo\Feefo
*/
public function fetch($params = null)
{
if ($params === null) {
$params['json'] = true;
$params['mode'] = 'both';
}
$params['logon'] = $this->config->get('feefo.logon');
$params['password'] = $this->config->get('feefo.password');
try {
$body = $this->client->get($this->getRequestUrl($params));
return $this->parse((string) $body->getBody());
} catch (Exception $e) {
throw $e; // Re-throw the exception
}
}
/**
* Parses the response.
*
* #param string $data
*
* #return \Illuminate\Support\Collection
*/
protected function parse($data)
{
$xml = new SimpleXMLElement($data);
foreach ((array) $xml as $items) {
if (isset($items->TOTALRESPONSES)) {
continue;
}
foreach ($items as $item) {
$this->data[] = new FeefoItem((array) $item);
}
}
return $this;
}
/**
* Assigns a value to the specified offset.
*
* #param mixed $offset
* #param mixed $value
*
* #return void
*/
public function offsetSet($offset, $value)
{
if (is_null($offset)) {
$this->data[] = $value;
} else {
$this->data[$offset] = $value;
}
}
/**
* Whether or not an offset exists.
*
* #param mixed $offset
*
* #return bool
*/
public function offsetExists($offset)
{
return isset($this->data[$offset]);
}
/**
* Unsets an offset.
*
* #param mixed $offset
*
* #return void
*/
public function offsetUnset($offset)
{
if ($this->offsetExists($offset)) {
unset($this->data[$offset]);
}
}
/**
* Returns the value at specified offset.
*
* #param mixed $offset
*
* #return mixed
*/
public function offsetGet($offset)
{
return $this->offsetExists($offset) ? $this->data[$offset] : null;
}
/**
* Count the number of items in the dataset.
*
* #return int
*/
public function count()
{
return count($this->data);
}
/**
* Get the instance as an array.
*
* #return array
*/
public function toArray()
{
return $this->data;
}
/**
* Returns the Feefo API endpoint.
*
* #param array $params
*
* #return string
*/
protected function getRequestUrl(array $params)
{
$query = http_build_query($params);
return sprintf('%s?%s', $this->config->get('feefo.baseuri'), $query);
}
}
And here is the code I'm using to try and use the fetch() method from the vendor class:
use Cms\Classes\ComponentBase;
use ArrayAccess;
use Countable;
use Exception;
use GuzzleHttp\Client;
use Illuminate\Contracts\Config\Repository;
use Illuminate\Contracts\Support\Arrayable;
use SimpleXMLElement;
use BlueBayTravel\Feefo\Feefo;
class Feedback extends ComponentBase
{
public $client;
public $config;
/**
* Container used for display
* #var BlueBayTravel\Feefo
*/
public $feedback;
public function componentDetails()
{
return [
'name' => 'Feedback Component',
'description' => 'Adds Feefo feedback to the website'
];
}
public function defineProperties()
{
return [];
}
public function onRun()
{
$this->feedback = $this->page['feedback'] = $this->loadFeedback($this->client, $this->config);
}
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
}
Won't allow me to call the Fetch() method statically so trying to instantiate and then use:
public function loadFeedback($client, $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
I've also tried type hinting the args like this:
public function loadFeedback(Client $client, Repository $config)
{
$feefo = new Feefo($client, $config);
$feedback = $feefo->fetch();
return $feedback;
}
But still I get the exception error above. I'm struggling to understand how to get past this. Any help for a newbie much appreciated :)
Just type hinting the function won't cast it to that object type. You need to properly pass the Guzzle\Client object to your function call.
// Make sure you 'use' the GuzzleClient on top of the class
// or use the Fully Qualified Class Name of the Client
$client = new Client();
$feedback = new Feedback();
// Now we passed the Client object to the function of the feedback class
// which will lead to the constructor of the Feefo class which is
// where your error is coming from.
$loadedFeedback = $feedback->loadFeedback($client);
Don't forget to do the same for the Repository $config from Laravel/Lumen
I am trying to insert data into db ,but it shows some error like this
My model Entity
Request.php
is here `<?php
namespace EvolisClientRequest\Model\Entities;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Request
{
/**
* #var \Ramsey\Uuid\Uuid
* #ORM\Id
* #ORM\Column(type="uuid")
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #ORM\ManyToOne(targetEntity="Client", inversedBy="request")
* #var Client
*/
private $client;
/**
* #ORM\ManyToMany(targetEntity="Status", inversedBy="request")
* #ORM\JoinTable(name="request_status")
* #var Status
*/
private $status;
/**
* #ORM\Column(type="integer")
* #var Qualification
*/
private $qualification;
/**
* #return \Ramsey\Uuid\Uuid
*/
public function getId()
{
return $this->id;
}
/**
* #return Salesperson
*/
public function getSalesperson()
{
return $this->salesperson;
}
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
/**
* #return Client
*/
public function getClient()
{
return $this->client;
}
/**
* #param Client $client
*/
public function setClient($client)
{
$this->client = $client;
}
/**
* #return Status
*/
public function getStatus()
{
return $this->status;
}
/**
* #param Status $status
*/
public function setStatus($status)
{
$this->status = $status;
}
/**
* #return Qualification
*/
public function getQualification()
{
return $this->qualification;
}
/**
* #param Qualification $qualification
*/
public function setQualification($qualification)
{
$this->qualification = $qualification;
}
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
}`
Also my
DAO "RequestBaseDao.php" is here,which is automatically generated.
<?php
/*
* This file has been automatically generated by Mouf/ORM.
* DO NOT edit this file, as it might be overwritten.
* If you need to perform changes, edit the RequestDao class instead!
*/
namespace EvolisClientRequest\Model\DAOs;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NonUniqueResultException;
use Mouf\Doctrine\ORM\Event\SaveListenerInterface;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestBaseDao class will maintain the persistence of Request class into the request table.
*
* #method Request findByQualification($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByQualification($fieldValue, $orderBy = null)
* #method Request findBySurfaceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMin($fieldValue, $orderBy = null)
* #method Request findBySurfaceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneBySurfaceMax($fieldValue, $orderBy = null)
* #method Request findByPriceMin($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMin($fieldValue, $orderBy = null)
* #method Request findByPriceMax($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByPriceMax($fieldValue, $orderBy = null)
* #method Request findByRequestDate($fieldValue, $orderBy = null, $limit = null, $offset = null)
* #method Request findOneByRequestDate($fieldValue, $orderBy = null)
*/
class RequestBaseDao extends EntityRepository
{
/**
* #var SaveListenerInterface[]
*/
private $saveListenerCollection;
/**
* #param EntityManagerInterface $entityManager
* #param SaveListenerInterface[] $saveListenerCollection
*/
public function __construct(EntityManagerInterface $entityManager, array $saveListenerCollection = [])
{
parent::__construct($entityManager, $entityManager->getClassMetadata('EvolisClientRequest\Model\Entities\Request'));
$this->saveListenerCollection = $saveListenerCollection;
}
/**
* Get a new persistent entity
* #param ...$params
* #return Request
*/
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
/**
* Peforms a flush on the entity.
*
* #param Request
* #throws \Exception
*/
public function save(Request $entity)
{
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->preSave($entity);
}
$this->getEntityManager()->flush($entity);
foreach ($this->saveListenerCollection as $saveListener) {
$saveListener->postSave($entity);
}
}
/**
* Peforms remove on the entity.
*
* #param Request $entity
*/
public function remove(Request $entity)
{
$this->getEntityManager()->remove($entity);
}
/**
* Finds only one entity. The criteria must contain all the elements needed to find a unique entity.
* Throw an exception if more than one entity was found.
*
* #param array $criteria
*
* #return Request
*/
public function findUniqueBy(array $criteria) : Request
{
$result = $this->findBy($criteria);
if (count($result) === 1) {
return $result[0];
} elseif (count($result) > 1) {
throw new NonUniqueResultException('More than one Request was found');
} else {
return;
}
}
/**
* Finds only one entity by Qualification.
* Throw an exception if more than one entity was found.
*
* #param mixed $fieldValue the value of the filtered field
*
* #return Request
*/
public function findUniqueByQualification($fieldValue)
{
return $this->findUniqueBy(array('qualification' => $fieldValue));
}
}
My RequestDao.php where i can write queries.
<?php
namespace EvolisClientRequest\Model\DAOs;
use EvolisClientRequest\Model\Entities\Request;
/**
* The RequestDao class will maintain the persistence of Request class into the request table.
*/
class RequestDao extends RequestBaseDao {
/*** PUT YOUR SPECIFIC QUERIES HERE !! ***/
public function setdata()
{
/*$product = new Request();
$product->setStatus('Keyboard');
$product->setClient('000000001ae10dda000000003c4667a6');
$product->setSalesperson('Ergonomic and stylish!');
$product->setQualification('1111');
//var_dump($r);die();
$em = $this->getEntityManager();
$em->persist($product);
$em->flush();*/
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
$this->save($product);
}
}
Finally my Controller "ContactController.php"
<?php
namespace EvolisClientRequest\Controllers;
use EvolisClientRequest\Model\DAOs\ClientDao;
use EvolisClientRequest\Model\Entities\Client;
use EvolisClientRequest\Model\Entities\Clients;
use EvolisClientRequest\Model\DAOs\RequestDao;
use EvolisClientRequest\Model\Entities\Request;
use EvolisClientRequest\Model\Entities\Requests;
use EvolisClientRequest\Model\DAOs\SalespersonDao;
use EvolisClientRequest\Model\Entities\Salesperson;
use EvolisClientRequest\Model\Entities\Salespersons;
use Mouf\Mvc\Splash\Annotations\Get;
use Mouf\Mvc\Splash\Annotations\Post;
use Mouf\Mvc\Splash\Annotations\Put;
use Mouf\Mvc\Splash\Annotations\Delete;
use Mouf\Mvc\Splash\Annotations\URL;
use Mouf\Html\Template\TemplateInterface;
use Mouf\Html\HtmlElement\HtmlBlock;
use Psr\Log\LoggerInterface;
use \Twig_Environment;
use Mouf\Html\Renderer\Twig\TwigTemplate;
use Mouf\Mvc\Splash\HtmlResponse;
use Doctrine\DBAL\DriverManager;
use Zend\Diactoros\Response\JsonResponse;
use Doctrine\Common\Collections\ArrayCollection;
/**
* TODO: write controller comment
*/
class ContactController {
/**
* The logger used by this controller.
* #var LoggerInterface
*/
private $logger;
/**
* The template used by this controller.
* #var TemplateInterface
*/
private $template;
/**
* The header of the page.
* #var HtmlBlock
*/
private $header;
/**
* The main content block of the page.
* #var HtmlBlock
*/
private $content;
/**
* The Twig environment (used to render Twig templates).
* #var Twig_Environment
*/
private $twig;
/**
* Controller's constructor.
* #param LoggerInterface $logger The logger
* #param TemplateInterface $template The template used by this controller
* #param HtmlBlock $content The main content block of the page
* #param Twig_Environment $twig The Twig environment (used to render Twig templates)
*/
public function __construct(LoggerInterface $logger, TemplateInterface $template, HtmlBlock $content, HtmlBlock $header, Twig_Environment $twig, ClientDao $clientDao, RequestDao $requestDao, SalespersonDao $salespersonDao) {
$this->logger = $logger;
$this->template = $template;
$this->content = $content;
$this->twig = $twig;
$this->header = $header;
$this->clientDao = $clientDao;
$this->requestDao = $requestDao;
$this->salespersonDao = $salespersonDao;
}
/**
* #URL("new.html")
*/
public function new() {
// TODO: write content of action here
// Let's add the twig file to the template.
$this->content->addHtmlElement(new TwigTemplate($this->twig, 'views/contact/new.twig', array("message"=>"world")));
$this->header->addHtmlElement(new TwigTemplate($this->twig, 'views/root/header.twig', array("message"=>"world")));
return new HtmlResponse($this->template);
}
/**
* #URL("saveData")
* For Saving the data
*/
public function saveData()
{
/*$newClient = $this->clientDao->create('hello', 'sarathchandran#122.com','8907263949');
$this->clientDao->save($newClient);*/
//$data = array();
//$data['salespersonDao']['salesperson'] = 'example#sales.com';
//$data['request']['qualification'] = 'abcdefgh';
//$newClient = $this->requestDao->create($data);
//$newClient = $this->requestDao->setQualification('Keyboard');
// $this->requestDao->save($newClient);
$user_data=$this->requestDao->setdata();
//return new JsonResponse([ "status"=>0 ]);
}
}
I am using Mouf framework.I am stuck with this problem.Someone Please help me to solve this problem.
Thanks in advance
As advised by #rokas, you should really start reading more about Doctrine. This is not a Mouf issue, this is clearly a Doctrine ORM issue so the appropriate doc is here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/index.html
Here are a few tips:
Your controller calls the setdata method
$user_data=$this->requestDao->setdata();
The setdata method calls:
$product= $this->create('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Now, the create method is:
public function create(...$params) : Request
{
$entity = new Request(...$params);
$this->getEntityManager()->persist($entity);
return $entity;
}
This means that it will call the Request constructor with this parameters:
$entity = new Request('Keyboard','000000001ae10dda000000003c4667a6','Ergonomic and stylish!','1111');
Have a look at the Request constructor:
public function __construct($salesperson, $client, $status, $qualification) {
$this->salesperson = $salesperson;
$this->client = $client;
$this->status = $status;
$this->qualification = $qualification;
}
As you can see, the first parameter is $salesperson. You try to put the value "Keyboard" here. The $salesperson attribute is defined this way:
/**
* #ORM\ManyToMany(targetEntity="Salesperson", inversedBy="request")
* #ORM\JoinTable(name="request_salesperson")
* #var Salesperson
*/
private $salesperson;
/**
* #param Salesperson $salesperson
*/
public function setSalesperson($salesperson)
{
$this->salesperson = $salesperson;
}
Now, here is your problem I think.
The $salesperson property is defined as a "ManyToMany" association. So you really cannot put a string in here, it is a collection of Salesperson. By the way, you should not "set" anything either. The setter should be completely removed.
Instead, you should consider using it as per the documentation here: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/working-with-associations.html
For instance:
$request->getSalesPersons()->add($someObjectRepresentingAPerson);
Also, notice that since the $salesperson represents a collection of Salesperson object, your should really name it $salesPersons (plural form)
I'm currently using Symfony2 to create (and learn how to) a REST API. I'm using FOSRestBundle and i've created an "ApiControllerBase.php" with the following :
<?php
namespace Utopya\UtopyaBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\View\View;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class ApiControllerBase
*
* #package Utopya\UtopyaBundle\Controller
*/
abstract class ApiControllerBase extends FOSRestController
{
/**
* #param string $entityName
* #param string $entityClass
*
* #return array
* #throws NotFoundHttpException
*/
protected function getObjects($entityName, $entityClass)
{
$dataRepository = $this->container->get("doctrine")->getRepository($entityClass);
$entityName = $entityName."s";
$data = $dataRepository->findAll();
foreach ($data as $object) {
if (!$object instanceof $entityClass) {
throw new NotFoundHttpException("$entityName not found");
}
}
return array($entityName => $data);
}
/**
* #param string $entityName
* #param string $entityClass
* #param integer $id
*
* #return array
* #throws NotFoundHttpException
*/
protected function getObject($entityName, $entityClass, $id)
{
$dataRepository = $this->container->get("doctrine")->getRepository($entityClass);
$data = $dataRepository->find($id);
if (!$data instanceof $entityClass) {
throw new NotFoundHttpException("$entityName not found");
}
return array($entityClass => $data);
}
/**
* #param FormTypeInterface $objectForm
* #param mixed $object
* #param string $route
*
* #return Response
*/
protected function processForm(FormTypeInterface $objectForm, $object, $route)
{
$statusCode = $object->getId() ? 204 : 201;
$em = $this->getDoctrine()->getManager();
$form = $this->createForm($objectForm, $object);
$form->submit($this->container->get('request_stack')->getCurrentRequest());
if ($form->isValid()) {
$em->persist($object);
$em->flush();
$response = new Response();
$response->setStatusCode($statusCode);
// set the `Location` header only when creating new resources
if (201 === $statusCode) {
$response->headers->set('Location',
$this->generateUrl(
$route, array('id' => $object->getId(), '_format' => 'json'),
true // absolute
)
);
}
return $response;
}
return View::create($form, 400);
}
}
This handles getting one object with a given id, all objects and process a form. But to use this i have to create as many controller as needed. By example : GameController.
<?php
namespace Utopya\UtopyaBundle\Controller;
use FOS\RestBundle\Controller\FOSRestController;
use FOS\RestBundle\Controller\Annotations as Rest;
use FOS\RestBundle\View\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Utopya\UtopyaBundle\Entity\Game;
use Utopya\UtopyaBundle\Form\GameType;
/**
* Class GameController
*
* #package Utopya\UtopyaBundle\Controller
*/
class GameController extends ApiControllerBase
{
private $entityName = "Game";
private $entityClass = 'Utopya\UtopyaBundle\Entity\Game';
/**
* #Rest\View()
*/
public function getGamesAction()
{
return $this->getObjects($this->entityName, $this->entityClass);
}
/**
* #param int $id
*
* #return array
* #throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* #Rest\View()
*/
public function getGameAction($id)
{
return $this->getObject($this->entityName, $this->entityClass, $id);
}
/**
* #return mixed
*/
public function postGameAction()
{
return $this->processForm(new GameType(), new Game(), "get_game");
}
}
This sound not so bad to me but there's a main problem : if i want to create another controller (by example Server or User or Character), i'll have to do the same process and i don't want to since it'll be the same logic.
Another "maybe" problem could be my $entityName and $entityClass.
Any idea or could i make this better ?
Thank-you !
===== Edit 1 =====
I think i made up my mind. For those basics controllers. I would like to be able to "configure" instead of "repeat".
By example i could make a new node in config.yml with the following :
#config.yml
mynode:
game:
entity: 'Utopya\UtopyaBundle\Entity\Game'
This is a very basic example but is it possible to make this and transform it into my GameController with 3 methods routes (getGame, getGames, postGame) ?
I just want some leads if i can really achieve with this way or not, if yes with what components ? (Config, Router, etc.)
If no, what could i do? :)
Thanks !
I'd like to show my approach here.
I've created a base controller for the API, and I stick with the routing that's generated with FOSRest's rest routing type. Hence, the controller looks like this:
<?php
use FOS\RestBundle\Controller\Annotations\View;
use \Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
use Psr\Log\LoggerInterface;
use Symfony\Component\Form\FormFactoryInterface,
Symfony\Bridge\Doctrine\RegistryInterface,
Symfony\Component\Security\Core\SecurityContextInterface,
Doctrine\Common\Persistence\ObjectRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
abstract class AbstractRestController
{
/**
* #var \Symfony\Component\Form\FormFactoryInterface
*/
protected $formFactory;
/**
* #var string
*/
protected $formType;
/**
* #var string
*/
protected $entityClass;
/**
* #var SecurityContextInterface
*/
protected $securityContext;
/**
* #var RegistryInterface
*/
protected $doctrine;
/**
* #var EventDispatcherInterface
*/
protected $dispatcher;
/**
* #param FormFactoryInterface $formFactory
* #param RegistryInterface $doctrine
* #param SecurityContextInterface $securityContext
* #param LoggerInterface $logger
*/
public function __construct(
FormFactoryInterface $formFactory,
RegistryInterface $doctrine,
SecurityContextInterface $securityContext,
EventDispatcherInterface $dispatcher
)
{
$this->formFactory = $formFactory;
$this->doctrine = $doctrine;
$this->securityContext = $securityContext;
$this->dispatcher = $dispatcher;
}
/**
* #param string $formType
*/
public function setFormType($formType)
{
$this->formType = $formType;
}
/**
* #param string $entityClass
*/
public function setEntityClass($entityClass)
{
$this->entityClass = $entityClass;
}
/**
* #param null $data
* #param array $options
*
* #return \Symfony\Component\Form\FormInterface
*/
public function createForm($data = null, $options = array())
{
return $this->formFactory->create(new $this->formType(), $data, $options);
}
/**
* #return RegistryInterface
*/
public function getDoctrine()
{
return $this->doctrine;
}
/**
* #return \Doctrine\ORM\EntityRepository
*/
public function getRepository()
{
return $this->doctrine->getRepository($this->entityClass);
}
/**
* #param Request $request
*
* #View(serializerGroups={"list"}, serializerEnableMaxDepthChecks=true)
*/
public function cgetAction(Request $request)
{
$this->logger->log('DEBUG', 'CGET ' . $this->entityClass);
$offset = null;
$limit = null;
if ($range = $request->headers->get('Range')) {
list($offset, $limit) = explode(',', $range);
}
return $this->getRepository()->findBy(
[],
null,
$limit,
$offset
);
}
/**
* #param int $id
*
* #return object
*
* #View(serializerGroups={"show"}, serializerEnableMaxDepthChecks=true)
*/
public function getAction($id)
{
$this->logger->log('DEBUG', 'GET ' . $this->entityClass);
$object = $this->getRepository()->find($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
}
return $object;
}
/**
* #param Request $request
*
* #return \Symfony\Component\Form\Form|\Symfony\Component\Form\FormInterface
*
* #View()
*/
public function postAction(Request $request)
{
$object = new $this->entityClass();
$form = $this->createForm($object);
$form->submit($request);
if ($form->isValid()) {
$this->doctrine->getManager()->persist($object);
$this->doctrine->getManager()->flush($object);
return $object;
}
return $form;
}
/**
* #param Request $request
* #param int $id
*
* #return \Symfony\Component\Form\FormInterface
*
* #View()
*/
public function putAction(Request $request, $id)
{
$object = $this->getRepository()->find($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
}
$form = $this->createForm($object);
$form->submit($request);
if ($form->isValid()) {
$this->doctrine->getManager()->persist($object);
$this->doctrine->getManager()->flush($object);
return $object;
}
return $form;
}
/**
* #param Request $request
* #param $id
*
* #View()
*
* #return object|\Symfony\Component\Form\FormInterface
*/
public function patchAction(Request $request, $id)
{
$this->logger->log('DEBUG', 'PATCH ' . $this->entityClass);
$object = $this->getRepository()->find($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
}
$form = $this->createForm($object);
$form->submit($request, false);
if ($form->isValid()) {
$this->doctrine->getManager()->persist($object);
$this->doctrine->getManager()->flush($object);
return $object;
}
return $form;
}
/**
* #param int $id
*
* #return array
*
* #View()
*/
public function deleteAction($id)
{
$this->logger->log('DEBUG', 'DELETE ' . $this->entityClass);
$object = $this->getRepository()->find($id);
if (!$object) {
throw new NotFoundHttpException(sprintf('%s#%s not found', $this->entityClass, $id));
}
$this->doctrine->getManager()->remove($object);
$this->doctrine->getManager()->flush($object);
return ['success' => true];
}
}
It has methods for most of RESTful actions. Next, when I want to implement a CRUD for an entity, that's what I do:
The abstract controller is registered in the DI as an abstract service:
my_api.abstract_controller:
class: AbstractRestController
abstract: true
arguments:
- #form.factory
- #doctrine
- #security.context
- #logger
- #event_dispatcher
Next, when I'm implementing a CRUD for a new entity, I create an empty class and a service definition for it:
class SettingController extends AbstractRestController implements ClassResourceInterface {}
Note that the class implements ClassResourceInterface. This is necessary for the rest routing to work.
Here's the service declaration for this controller:
api.settings.controller.class: MyBundle\Controller\SettingController
api.settings.form.class: MyBundle\Form\SettingType
my_api.settings.controller:
class: %api.settings.controller.class%
parent: my_api.abstract_controller
calls:
- [ setEntityClass, [ MyBundle\Entity\Setting ] ]
- [ setFormType, [ %my_api.settings.form.class% ] ]
Then, I'm just including the controller in routing.yml as the FOSRestBundle doc states, and it's done.