Cakephp .json ext giving Missing Method - cakephp

I have a dropdown that onChange I use ajax to load some .json from a method in a controller.
However I am getting error 404 returned
If I remove the .json extension I get error 500 missing template which I have not been able to resolve either. I have tried different solution. I would rather use the .json ext anyway and let cakephp return the correct formatted JSON.
Missing Method in StrategiesConditionsController
Cake\Controller\Exception\MissingActionException
Error The action conditions.json is not defined in StrategiesConditionsController
Create StrategiesConditionsController::conditions.json() in file: src/Controller/StrategiesConditionsController.php.
From reading the doc a couple of times I am sure I have the routes correct.
routes.php
<?php
/**
* Routes configuration.
*
* In this file, you set up routes to your controllers and their actions.
* Routes are very important mechanism that allows you to freely connect
* different URLs to chosen controllers and their actions (functions).
*
* It's loaded within the context of `Application::routes()` method which
* receives a `RouteBuilder` instance `$routes` as method argument.
*
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
*
* Licensed under The MIT License
* For full copyright and license information, please see the LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* #copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
* #link https://cakephp.org CakePHP(tm) Project
* #license https://opensource.org/licenses/mit-license.php MIT License
*/
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Routing\Route\DashedRoute;
use Cake\Routing\RouteBuilder;
use Cake\Routing\Router;
/*
* The default class to use for all routes
*
* The following route classes are supplied with CakePHP and are appropriate
* to set as the default:
*
* - Route
* - InflectedRoute
* - DashedRoute
*
* If no call is made to `Router::defaultRouteClass()`, the class used is
* `Route` (`Cake\Routing\Route\Route`)
*
* Note that `Route` does not do any inflections on URLs which will result in
* inconsistently cased URLs when used with `:plugin`, `:controller` and
* `:action` markers.
*/
/** #var \Cake\Routing\RouteBuilder $routes */
$routes->setRouteClass(DashedRoute::class);
$routes->scope('/', function (RouteBuilder $routes) {
$routes->setExtensions(['json']);
$routes->resources('StrategiesConditions', [
'map' => [
'conditions' => [
'action' => 'conditions',
'method' => 'post'
]
],
'only' => ['conditions']
]);
});
$routes->scope('/', function (RouteBuilder $builder) {
// Register scoped middleware for in scopes.
$builder->registerMiddleware('csrf', new CsrfProtectionMiddleware([
'httpOnly' => true,
]));
/*
* Apply a middleware to the current route scope.
* Requires middleware to be registered through `Application::routes()` with `registerMiddleware()`
*/
$builder->applyMiddleware('csrf');
/*
* Here, we are connecting '/' (base path) to a controller called 'Pages',
* its action called 'display', and we pass a param to select the view file
* to use (in this case, templates/Pages/home.php)...
*/
$builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']);
/*
* ...and connect the rest of 'Pages' controller's URLs.
*/
$builder->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']);
/*
* Connect catchall routes for all controllers.
*
* The `fallbacks` method is a shortcut for
*
* ```
* $builder->connect('/:controller', ['action' => 'index']);
* $builder->connect('/:controller/:action/*', []);
* ```
*
* You can remove these routes once you've connected the
* routes you want in your application.
*/
$builder->fallbacks();
});
Router::prefix('admin', function (RouteBuilder $routes) {
$routes->connect('/', ['controller' => 'Users', 'action' => 'index']);
$routes->fallbacks(DashedRoute::class);
});
/*
* If you need a different set of middleware or none at all,
* open new scope and define routes there.
*
* ```
* $routes->scope('/api', function (RouteBuilder $builder) {
* // No $builder->applyMiddleware() here.
* // Connect API actions here.
* });
* ```
*/
strategiesConditionsController
public function conditions()
{
$this->request->allowMethod(['post']);
if ($this->request->is('ajax')) {
$this->response = $this->response->withDisabledCache();
}
$strategy_id = $this->request->getData('strategy_id');
$strategiesConditions = $this->StrategiesConditions->find('all', [
'where' => ['strategy_id' => $strategy_id],
]);
$this->viewBuilder()->setOption('serialize', ['strategiesConditions']);
}
Cakephp console has 8 routes showing
Route name URI template Defaults
pages:display /
{
"controller": "Pages",
"action": "display",
"0": "home",
"plugin": null
}
pages:display /pages/*
{
"controller": "Pages",
"action": "display",
"plugin": null
}
_controller:index /{controller}
{
"action": "index",
"plugin": null
}
_controller:_action /{controller}/{action}/*
{
"plugin": null,
"action": "index"
}
admin:users:index /admin
{
"controller": "Users",
"action": "index",
"prefix": "Admin",
"plugin": null
}
admin:_controller:index /admin/{controller}
{
"action": "index",
"prefix": "Admin",
"plugin": null
}
admin:_controller:_action /admin/{controller}/{action}/*
{
"prefix": "Admin",
"plugin": null,
"action": "index"
}
strategiesconditions:conditions /strategies-conditions/conditions
{
"controller": "StrategiesConditions",
"action": "conditions",
"_method": "post",
"plugin": null
}
Update from the history tab in debug kit
Request
Routing Params
controller StrategiesConditions
action conditions.json
pass (empty)
plugin (null)
_matchedRoute /{controller}/{action}/*
_ext (null)
Post data
strategy_id 11
Query string
No querystring data.
Cookie
csrfToken dd12f852560a384d39206e511f1857f77f71da2eadb023a6c67ae346
PHPSESSID 3d2jt7dgpo09af8b1908quljpn
Matched Route
template /{controller}/{action}/*
and the routes tab:
Routes
Toggle debugkit internal routes
Route name URI template Defaults
pages:display /
{
"controller": "Pages",
"action": "display",
"0": "home",
"plugin": null
}
pages:display /pages/*
{
"controller": "Pages",
"action": "display",
"plugin": null
}
_controller:index /{controller}
{
"action": "index",
"plugin": null
}
_controller:_action /{controller}/{action}/*
{
"plugin": null,
"action": "index"
}
admin:users:index /admin
{
"controller": "Users",
"action": "index",
"prefix": "Admin",
"plugin": null
}
admin:_controller:index /admin/{controller}
{
"action": "index",
"prefix": "Admin",
"plugin": null
}
admin:_controller:_action /admin/{controller}/{action}/*
{
"prefix": "Admin",
"plugin": null,
"action": "index"
}
strategiesconditions:conditions /strategies-conditions/conditions
{
"controller": "StrategiesConditions",
"action": "conditions",
"_method": "post",
"plugin": null
}
Request URL
http://localhost:8888/trading-journal/strategies-conditions/conditions.json
Application.php
// Add routing middleware.
// If you have a large number of routes connected, turning on routes
// caching in production could improve performance. For that when
// creating the middleware instance specify the cache config name by
// using it's second constructor argument:
// `new RoutingMiddleware($this, '_cake_routes_')`
->add(new RoutingMiddleware($this))

'method' => 'POST' should have been uppercase. This was not document. A pull request has been made to change so it is not case sensitive.
$routes->scope('/', function (RouteBuilder $routes) {
$routes->setExtensions(['json']);
$routes->resources('StrategiesConditions', [
'map' => [
'conditions' => [
'action' => 'conditions',
'method' => 'POST'
]
],
'only' => ['conditions']
]);
});

Related

Laravel - Show data from a specific user

I'm trying to create a listing that will only show lyrics from the logged user, but no succeed. Need help please.
Sorry for the image link, but my reputation is still low to add images.
As you can see I have two lyrics menus, one is already accessible only for admin users and the other to show the lyrics list for authenticated user.
How can I do that?
LyricController
{
public function __construct(){
$this->middleware('auth');
$this->middleware('can:admin-content');
}
/**
* Display a listing of the resource.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
$lyrics = Lyric::orderBy('lyric', 'ASC')->paginate('10');
return view('admin.lyrics.index', [
'lyrics' => $lyrics,
]);
}
/**
* Show the form for creating a new resource
*
* #return \Illuminate\Http\Response
*/
public function create()
{
// load the create form (app/views/lyrics/create.blade.php)
return view('admin.lyrics.create');
}
/**
* Store a newly created resource in storage.
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->only([
'title',
// 'artist',
'info',
'video_url',
'lyric'
]);
$data['slug'] = Str::slug($data['title'], '-');
$validator = Validator::make($data, [
'title' => ['required', 'string', 'max:100'],
'slug' => ['required', 'string', 'max:100', 'unique:lyrics'],
// 'artist' => ['required', 'string', 'max:200'],
'info' => ['string', 'max:100'],
'video_url' => ['required', 'string', 'max:100', 'unique:lyrics'],
'lyric' => ['required', 'string'],
]);
if ($validator->fails()) {
return redirect()->route('lyrics.create')
->withErrors($validator)
->withInput();
}
// $artist = new Singer;
// $artist->artist = $data['artist'];
// $artist->save();
$lyric = new Lyric;
$lyric->title = trim($data['title']);
$lyric->slug = $data['slug'];
$lyric->info = $data['info'];
$lyric->video_url = $data['video_url'];
$lyric->lyric = $data['lyric'];
$lyric->save();
Session::flash('message', 'Música adicionada com sucesso!');
return redirect()->route('lyrics.index');
}
/**
* Display the specified resource
*
*
.
*
* #param \App\Lyric $lyric
* #return \Illuminate\Http\Response
*/
public function show($id)
{
$lyric = Lyric::find($id);
return view('admin.lyrics.show', [
'lyric' => $lyric
]);
}
/**
* Show the form for editing the specified resource.
*
* #param \App\Lyric $lyric
* #return \Illuminate\Http\Response
*/
public function edit($id)
{
$lyric = Lyric::find($id);
if ($lyric) {
return view('admin.lyrics.edit', [
'lyric' => $lyric
]);
}
return redirect()->route('lyrics.index');
}
/**
* Update the specified resource in storage.
*
* #param \Illuminate\Http\Request $request
* #param \App\Lyric $lyric
* #return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$lyric = Lyric::find($id);
if ($lyric) {
$data = $request->only([
'title',
// 'artist',
'info',
'video_url',
'lyric'
]);
if ($lyric['title'] !== $data['title']) {
$data['slug'] = Str::slug($data['title'], '-');
$validator = Validator::make($data, [
'title' => ['required', 'string', 'max:100'],
'info' => ['string', 'max:100'],
'video_url' => ['required', 'string', 'max:100', 'url'],
'slug' => ['required', 'string', 'max:100', 'unique:lyrics'],
'lyric' => ['string'],
]);
} else {
$validator = Validator::make($data, [
'title' => ['required', 'string', 'max:100'],
'info' => ['string', 'max:100'],
'video_url' => ['required', 'string', 'max:100', 'url'],
'lyric' => ['string'],
]);
}
if ($validator->fails()) {
return redirect()->route('lyrics.edit', [
'lyric' => $id
])
->withErrors($validator)
->withInput();
}
$lyric->title = trim($data['title']);
$lyric->info = $data['info'];
$lyric->video_url = $data['video_url'];
$lyric->lyric = $data['lyric'];
if (!empty($data['slug'])) {
$lyric->slug = $data['slug'];
}
$lyric->save();
}
Session::flash('message', 'Música alterada com sucesso!');
return redirect()->route('lyrics.index');
}
/**
* Remove the specified resource from storage.
*
* #param \App\Lyric $lyric
* #return \Illuminate\Http\Response
*/
public function destroy($id)
{
$lyric = Lyric::find($id);
$lyric->delete();
Session::flash('message', 'Música excluída com sucesso!');
return redirect()->route('lyrics.index');
}
Edit: I have just added foreign key to the Users Migration as suggested
Route: web.php
Route::get('/', 'Site\HomeController#index')->name('home');
Auth::routes();
Route::prefix('painel')->group(function(){
Route::get('/', 'Admin\HomeController#index')->name('admin');
/* Login Routing */
Route::get('/login', 'Admin\Auth\LoginController#index')->name('login');
Route::post('/login', 'Admin\Auth\LoginController#authenticate');
/* Logout Route */
Route::post('/logout', 'Admin\Auth\LoginController#logout')->name('logout');
Route::get('/logout', 'Admin\Auth\LoginController#logout')->name('logout-get');
/* Register Routing */
Route::get('/register', 'Admin\Auth\RegisterController#index')->name('register');
Route::post('/register', 'Admin\Auth\RegisterController#register');
/* Users Routing */
Route::resource('/users', 'Admin\UserController');
/* Profile Routing */
Route::get('/profile', 'Admin\ProfileController#index')->name('profile');
Route::put('/profilesave', 'Admin\ProfileController#save')->name('profile.save');
/* Lyrics Routing */
Route::resource('/lyrics', 'Admin\LyricController');
});
User.php (model):
public function lyrics()
{
return $this->hasMany(Lyric::class)->withTimestamps();
}
Lyric.php (model):
protected $guarded = ['id', 'singer_id', 'created_at', 'updated_at'];
public function singer()
{
return $this->belongsTo(Singer::class)->withTimestamps();
}
public function user()
{
return $this->belongsTo(User::class);
}
It would be really helpful if you could add more details in you question, but let me suggest a scenario to give you an idea of how it might be done:
Assuming you have Passport or JWT for user authentication, and that your user is logged in through api, and that you have 'Lyrics' model with user_id foreign key, you can do the following :
$logged_user = auth()->guard('api')->user();
if(isset($logged_user)){
$lyrics = Lyrics::where('user_id',$logged_user->id)->get();
//do whatever you need with lyrics
}
else{
// user not authenticated
return false;
}
More on authentication from Laravel documentation here
To show data from a logged in User. You need to establish an eloquent relationship between the User Model and The lyrics model.
Then you could just do something like $user -> lyrics()

Cakephp 3 PHPUnit ingration test fails after the test before

I have the UsersFixture with three records.
The test methods first() and second(), which both are before guest_can_login(), pr's show "Joe", "Joe", as expected. But with test method third(), which comes after guest_can_login(), I get notice error: trying to get property of non-object.
So, for a reason, something in the guest_can_login() breaks the rest of the test methods. I have tried by making a duplicate of guest_can_login() as well.
I think it is strange, as tearDown should "reset" everything after each test. I'm out of ideas. And after reading the Cakephp Testing docs, I haven't been able to solve it.
Any suggestions to help me solve this is much appreciated.
Code below (gist if you prefer: https://gist.github.com/chris-andre/2eb3ad053073caf4f1c81722428a900b):
public $fixtures = [
'app.users',
'app.tenants',
'app.roles',
'app.roles_users',
];
public $Users;
public function setUp()
{
parent::setUp();
$config = TableRegistry::getTableLocator()->exists('Users') ? [] : ['className' => UsersTable::class];
$this->Users = TableRegistry::getTableLocator()->get('Users', $config);
}
/**
* tearDown method
*
* #return void
*/
public function tearDown()
{
unset($this->Users);
TableRegistry::clear();
parent::tearDown();
}
/** #test */
public function guest_can_register()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true
];
$this->post('/register', $data);
$this->assertResponseSuccess();
$this->assertRedirect(['controller' => 'Users', 'action' => 'login', '_host' => 'testcomp.timbas.test']);
$user = $this->Users->find()
->contain(['Tenants', 'Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first();
}
/** #test */
public function first()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function second()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
/** #test */
public function guest_can_login()
{
$this->enableCsrfToken();
$this->enableSecurityToken();
$this->configRequest([
'headers' => [
'host' => 'testcomp.timbas.test'
]
]);
$data = [
'email' => 'chris#andre.com',
'first_name' => 'Christian',
'last_name' => 'Andreassen',
'password' => '123456',
'tenant' => ['name' => 'Test Company AS', 'domain' => 'testcomp', 'active' => true],
'active' => true,
'roles' => ['_ids' => [ADMINISTRATOR_ROLE_ID]]
];
$user = $this->Users->newEntity($data, [
'associated' => ['Tenants', 'Roles']
]);
$this->Users->save($user);
$getNewUser = $this->Users->find()
->contain(['Roles'])
->where(['Users.email' => 'chris#andre.com'])
->first()
->toArray();
// pr($getNewUser->id);
$this->post('/users/login', [
'email' => 'chris#andre.com',
'password' => '123456'
]);
$this->assertSession($getNewUser, 'Auth.User');
}
/** #test */
public function third()
{
$users = $this->Users->find()->first();
pr($users->first_name);
}
EDIT 2018-08-06:
Users::register() is a global context, I cannot be accessed from url with subdomain. E.g. tenant1.domain.com/register will throw a badRequest, while domain.com/register is a valid url. On registration success, user is forwarded to login from right url. Login-url = Tenants.domain + domain + suffix, e.g. tenant1.domain.com. When user is on the tenant scope (url with subdomain), the Tenants.id where Tenants.domain = tenant1, will be added to the where clause in all queries for the models having the behavior attached.
Now, what happens in third() is that the tenant_id from the newly created Tenant in guest_can_login() is added to the query, which means the test "is still on the tenant scope" when third() is run. That is the problem.
The other problem is that setUp() is called on all test methods but third(). testDown() is called on every test methods.
App\Middleware\TenantMiddleware.php:
use InstanceConfigTrait;
/**
* Default config.
* Options:
* - globalScope: tells the middleware what controller and action tenant scope is not being used
* Example
* 'globalScope' => [
* 'Pages' => ['*'], // All actions in PagesController is global
* 'Users' => ['register'] // Register action in UsersController is global
* ]
* #var array
*/
protected $_defaultConfig = [
'globalScope' => [
'Users' => ['register'],
'Landing' => ['*'],
'Pages' => ['*']
],
];
public function __construct($config = [])
{
if (!isset($config['primaryDomain'])) {
$config['primaryDomain'] = Configure::read('Site.domain');
}
$this->setConfig($config);
}
/**
* Invoke method.
*
* #param \Cake\Http\ServerRequest $request The request.
* #param \Psr\Http\Message\ResponseInterface $response The response.
* #param callable $next Callback to invoke the next middleware.
* #return \Psr\Http\Message\ResponseInterface A response
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next)
{
// Get subdomains
$subdomains = $request->subdomains();
// If subdomains not empty, the first is always the tenants domain
$subdomain = !empty($subdomains) ? $subdomains[0] : '';
Tenant::setDomain($subdomain);
// Get params of current request
$params = $request->getAttribute('params');
$controller = $params['controller'];
$action = $params['action'];
// Set tenantScope as default
Tenant::setScope('tenant');
$globalScope = $this->getConfig('globalScope');
// If Controller and action is a global scope
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) || in_array('*', $globalScope[$controller])) {
Tenant::setScope('global');
}
}
if (
(Tenant::getScope() === 'tenant' && Tenant::tenant() === null)
|| (Tenant::getDomain() === '' && Tenant::getScope() === 'tenant')
|| (Tenant::getDomain() !== '' && Tenant::getScope() === 'global')
) {
throw new NotFoundException('The page you are looking for does not exists.');
}
$primaryDomain = $this->getConfig('primaryDomain');
if (array_key_exists($controller, $globalScope)) {
if (in_array($action, $globalScope[$controller]) && Tenant::getScope() === 'global') {
}
}
return $next($request, $response);
}
App\Model\Behavior\TenantScopeBehavior.php:
protected $_table;
/**
* Default configuration.
*
* #var array
*/
protected $_defaultConfig = [];
public function __construct(Table $table, array $config = [])
{
parent::__construct($table, $config);
}
public function beforeFind(Event $event, Query $query, ArrayObject $options)
{
$model = $this->_table->getAlias();
$foreig_key = 'tenant_id';
if (!isset($options['skipTenantCheck']) || $options['skipTenantCheck'] !== true) {
if (Tenant::getScope() === 'tenant') {
if ($model === 'Tenants') {
$query->where(['Tenants.id' => Tenant::tenant()->id]);
} else {
$query->where([$model . '.' . $foreig_key => Tenant::tenant()->id]);
}
}
}
return $query;
}
public function beforeSave(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->isNew()) {
$entity->tenant_id = Tenant::tenant()->id;
} else {
// Check if current tenant is owner
if ($this->_table->getAlias() === 'Tenants') {
if ($entity->id != Tenant::tenant()->id) {
throw new BadRequestException();
}
} else {
if ($entity->tenant_id != Tenant::tenant()->id) {
throw new BadRequestException();
}
}
}
}
return true;
}
public function beforeDelete(Event $event, Entity $entity, $options)
{
if (Tenant::getScope() === 'tenant') {
if ($entity->tenant_id != Tenant::tenant()->id) { //current tenant is NOT owner
throw new BadRequestException();
}
}
return true;
}
App\Tenant\Tenant.php:
/**
* $_domain will be empty or comtain the domain (subdomain from url)
* #var string can be empty
*/
protected static $_domain;
/**
* $_scope shall be 'global' or 'tenant'.
* #var string
*/
protected static $_scope;
/**
* #var null|object \App\Model\Entity\Tenant
*/
protected static $_tenant;
/**
* Gets domain from $_domain and returns the string
* #return string
*/
public static function getDomain()
{
return self::$_domain;
}
/**
* Set the tenant scope domain. Will be set in the TenantMiddleware, and shall not be set anywhere else
* #param string $domain
* #return string empty or with domain
*/
public static function setDomain($domain)
{
self::$_domain = $domain;
}
/**
* Tenant method
* Return the object \App\Model\Table\Tenants ro null
* #return type
*/
public static function tenant()
{
$tenant = static::_getTenant();
return $tenant;
}
protected static function _getTenant()
{
if (self::$_tenant === null) {
$cachedTenants = Cache::read('tenants');
if($cachedTenants !== false) {
// do something
}
$tenantsTable = TableRegistry::get('Tenants');
$tenant = $tenantsTable->find('all', ['skipTenantCheck' => true])
->where(['Tenants.domain' => self::getDomain()])
->where(['Tenants.active' => true])
->first();
self::$_tenant = $tenant;
}
return self::$_tenant;
}
public static function getScope()
{
return self::$_scope;
}
/**
* Description
* #param type $scope
* #return type
*/
public static function setScope($scope)
{
self::$_scope = $scope;
}

laravel request is undefined

I am a newbie in Laravel, so everything is under exploration period. I use angular http post to send data over to laravel and in laravel controller i am able to
dd($request)
Request {#40
#json: ParameterBag {#32
#parameters: array:4 [
"GPTour_id" => 1
"customer_id" => 1
"status" => "Confirmed"
"note" => "asdfasdf"
]
}
#userResolver: Closure {#300
class: "Illuminate\Auth\AuthServiceProvider"
this: AuthServiceProvider {#22 …}
use: array:1 [
"$app" => Application {#3
#basePath: "/Users/haophung/Dropbox/server/websites/nglaravelyep/laravel-backend"
#hasBeenBootstrapped: true
#booted: true
#bootingCallbacks: []
However, if i use
$request->input('key')
i got $request is undefined. Please advise!!!
public function addGospelCustomer(Request $request)
{
if ($request) {
$customer_id = $request->get('customer_id');
$tour_id = $request->get('GPTour_id');
$validator = Validator::make($request->all(), [
'customer_id' =>'required'
]);
if ($validator->fails()) {
return response()->json(['error' => $validator->errors()], 406);
}
$gospel_customer = Gospel_tour::find($tour_id)->with(['customers' => function($query) {
$query->where('id', $customer_id);
}])->first();
if ($gospel_customer === 'null') {
return response()->json(['error' => "The Customer is already on the list"], 406);
}
return 'success';//response()->json(['success' => $request], 200);
}else {
return response()->json(['error' =>'can not add customer'], 401);
}
}
ErrorException in GospelController.php line 60:
Undefined variable: customer_id
I think the problem is
$gospel_customer = Gospel_tour::find($tour_id)->with(['customers' => function($query) {
$query->where('id', $customer_id);
}])->first();
I can echo $customer_id out, but in this eloquent is not defined
You need to typehint requestion in your function definition
public function name(Request $request) {}
And use it like
$key = $request->key;
$key = $request->get('key');
Or use the global function
$key = request('key');
Update
Where you have the error exception do
$gospel_customer = Gospel_tour::find($tour_id)->with(['customers' => function($query) use ($customer_id) {
$query->where('id', $customer_id);
}]);
The error occurs because you are inside a closure, and it doesn't have access to external variables.

Loading large data from database with Symfony2 and Doctrine 2

I'm beginner on Symfony2.
I have a Regions-Countries-States-Cities database with more of 2,000,000 results. I have 8 entities:
Region (recursive with itself) - RegionTranslation
Country - CountryTranslation
State (recursive with itself) - StateTranslation
City - CityTranslation
The thing is that when I want to load a countries list (only 250 registers in a pulldown, for example) Symfony+Doctrine load all entities structure (all states of all countries, and all cities of all states, with their respective translations).
I think that it spends a lot of memory.
What's the correct method to do it? Can I load only Country (and translations) with this structure? Any idea?
I have had the same problem for objected that are unassociated. Your best bet is to use select2's ajax loading (http://ivaynberg.github.com/select2/), which will give a limited number of items in the search box, and also narrow searches down by what is typed in the box.
A few things things need to be coded:
A javascript file:
$(document).ready(function(){
$('.select2thing').select2({
minimumInputLength:1
,width: "100%"
,ajax: {
url: <<path>> + "entity/json"
,dataType: 'jsonp'
,quitMillis: 100
,data: function (term, page) {
return {
q: term, // search term
limit: 20,
page: page
};
}
,results: function (data, page) {
var more = (page * 20) < data.total;
return { results: data.objects, more: more };
}
}
});
}
A jsonAction in the controller:
/**
* Lists all Thing entities return in json format
*
*/
public function jsonAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$rep = $em->getRepository('yourBundle:Thing');
$qb = $rep->createQueryBuilder('e');
$limit = $request->query->get('limit');
$current = $request->query->get('current');
$page=$request->query->get('page');
$queries=$request->query->get('q');
$qarray=explode(",", $queries);
$entities=$rep->getJSON($qarray, $page, $limit);
$total=$rep->getJSONCount($qarray);
$callback=$request->query->get('callback');
return $this->render('yourBundle:Thing:json.html.twig'
, array(
'entities' => $entities
,'callback' => $callback
,'total' => $total
)
);
}
A twig template (json.html.twig, possibly customized to display more)
{{callback}}(
{ "objects" :
[
{% for entity in entities %}
{ "id": "{{entity.id}}", "text": "{{entity}}""}
{% if not loop.last %},{% endif %}
{% endfor %}
],
"total": {{total}}
}
)
A transformer:
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use yourBundle\Entity\Thing;
class ThingTransformer implements DataTransformerInterface
{
/**
* #var ObjectManager
*/
private $em;
/**
* #param ObjectManager $em
*/
public function __construct(ObjectManager $em)
{
$this->em = $em;
}
/**
* Transforms an object (thing) to a string (id).
*
* #param Issue|null $thing
* #return string
*/
public function transform($thing)
{
if (null === $thing) {return "";}
if (is_object($thing) && "Doctrine\ORM\PersistentCollection"==get_class($thing)){
$entity->map(function ($ob){return $ob->getId();});
return implode(",",$thing->toArray());
}
return $thing;
}
/**
* Transforms a string (id) to an object (thing).
*
* #param string $id
* #return Issue|null
* #throws TransformationFailedException if object (thing) is not found.
*/
public function reverseTransform($id)
{
if (!$id) {
return null;
}
//if (is_array($id)){
$qb=$this->em
->getRepository('yourBundle:Thing')
->createQueryBuilder('t');
$thing=$qb->andWhere($qb->expr()->in('t.id', $id))->getQuery()->getResult();
if (null === $entity) {
throw new TransformationFailedException(sprintf(
'A thing with id "%s" does not exist!',
$id
));
}
return $thing;
}
}
Your Controller using the select2 control will have to pass the 'em' to the form builder:
$editForm = $this->createForm(new ThingType()
,$entity
,array(
'attr' => array(
'securitycontext' => $sc
,'em' => $this->getDoctrine()
->getEntityManager()
)
)
);
And in your form type:
if (isset($options['attr']['em'])){ $em = $options['attr']['em'];} else {$em=null;}
$transformer = new ThingTransformer($em);
$builder->add(
$builder->create('thing'
,'hidden'
,array(
'by_reference' => false
,'required' => false
,'attr' => array(
'class' => 'select2thing'
)
)
)
->prependNormTransformer($transformer)
);
You can try to change the hydration mode, using array consumes less memory than creating objects.
Other way you can achieve this is using iterations to avoid memory problems:
Finally I think you can't load all without spending a lot of time and memory, so, why not make several queries to load the whole data?

Cakephp and Ajax-Post

I've set up cake php for ajax and json-requests using mostly this turtorial:
http://book.cakephp.org/2.0/en/views/json-and-xml-views.html
Everything is working fine, but if I make a post request (in this case for using cakephp with json-rpc), Cake enters an infite Loop saveing hundreds of empty entries in my database until it finally runs out of memory even if my controller and is completly empty, as long there is some json-output (even an 505 error message works). Is this due to automagic?
I am able to save data as intentend, when I pass data which is properly set up to fit the model. But if send anything else (just params or empty data for example) I enter this infite loop.
Even if my posts contain errors, I think this should not happen.
Here is the error message:
<b>Fatal error</b>: Allowed memory size of 33554432 bytes exhausted (tried to allocate 71 bytes) in <b>/var/www/****/cake/lib/Cake/Controller/Controller.php</b> on line <b>333</b><br />
Here is my call with jquery:
$.ajax({
url: '/dezem/cake/users.json', //even if I send the data to user without the json extension, the same happens...
type: 'POST',
dataType: 'json',
data: '{"method": "echo", "params": ["Hello JSON-RPC"], "id": 1}',
success: function(){alert("YEEHHEHAAW")},
error: function(){alert("Nööööööööööööööö")}
})
As requested here the Code inside my controller:
class UsersController extends AppController {
public $helpers = array('Js', 'Html');
public $components = array('RequestHandler');
....
public function index() {
$this->User->recursive = -1;
$users = $this->User->find('all');
if($this->RequestHandler->isAjax()){
$this->autoLayout = $this->autoRender = false;
if ($this->request->is('post')){
$this->set(array(
'data' => $users,
'_serialize' => array('data')
));
$this->render('/layouts/json');
}
else if ($this->request->is('get')) {
$this->set(array(
'data' => $users,
'_serialize' => array('data')
));
$this->render('/layouts/json');
}
}
}
}
As I said, an empty controller leads to the same result:
public function index() {
}

Resources