Base Table not found on unique value validation in MongoDB with laravel - mysql

I'm using laravel 5.3 with jenssegers/laravel-mongodb package for managing mongodb connections.
I want to check every time a user send a request to register a website in my controller if it's unique then let the user to register his/her website domain.
I wrote below code for validation but What I get in result is :
SQLSTATE[42S02]: Base table or view not found: 1146 Table 'iranad.seat' doesn't exist (SQL: select count(*) as aggregate from `seat` where `domain` = order.org)
my controller code :
public function store(Request $request) {
$seat = new Seat();
$validator = Validator::make($request->all(), [
'domain' => 'required|regex:/^([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/|unique:seat', //validating user is entering correct url like : iranad.ir
'category' => 'required',
]);
if ($validator->fails()) {
return response()->json($validator->messages(), 400);
} else {
try {
$statusCode = 200;
$seat->user_id = Auth::user()->id;
$seat->url = $request->input('domain');
$seat->cats = $request->input('category');
$seat->filter = [];
if($request->input('category') == 'all') {
$obj['cats'] = 'false';
$seat->target = $obj;
} else {
$obj['cats'] = 'true';
$seat->target = $obj;
}
$seat->status = 'Waiting';
$seat->save();
} catch (\Exception $e) {
$statusCode = 400;
} finally {
$response = \Response::json($seat, $statusCode);
return $response;
}
}
}
My Seat Model :
namespace App;
use Moloquent;
use Carbon\Carbon;
class Seat extends Moloquent {
public function getCreatedAtAttribute($value) {
return Carbon::createFromTimestamp(strtotime($value))
->timezone('Asia/Tehran')
->toDateTimeString();
}
}
Obviously The validator is checking if domain is unique in mysql tables which causes this error, How can I change my validation process to check mongodb instead of mysql ?

I solved the problem, The solution is that you should add Moloquent to your model and define database connection :
namespace App\Models;
use Moloquent;
use Carbon\Carbon;
class Seat extends Moloquent
{
protected $collection = 'seat';
protected $connection = 'mongodb';
}

Related

Laravel user model not being process in JSON response

I have a Laravel 5.8 API where the JSON response for a user collection works as expected but fails for a model.
namespace App\Traits;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
trait ApiResponder
{
private function successResponse($data, $code)
{
return response()->json($data, $code);
}
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
protected function showAll(Collection $collection, $code = 200)
{
return $this->successResponse(['data' => $collection], $code);
}
protected function showOne(Model $model, $code = 200)
{
return $this->successResponse(['data' => $model], $code);
}
}
Below are the controller methods calling for the response.
public function index()
{
$users = User::all();
return $this->showAll($users);
}
public function update(Request $request, $id)
{
$user = User::findOrFail($id);
$rules = [
'email' => 'email|unique:users,email,' . $user->id,
'password' => 'min:6|confirmed'
];
if ($request->has('name')) {
$user->name = $request->name;
}
if ($request->has('email') && $user->email != $request->email) {
$user->verififed = User::UNVERIFIED_USER;
$user->verififcation_token = User::generateVerificationCode();
$user->email = $request->email;
}
if ($request->has('password')) {
$user->password = bcrypt($request->password);
}
if (!$user->isDirty()) {
return $this->errorResponse('You need to specify a change to update', 422);
}
$user->save();
$this->showOne($user);
}
The index method handle as a collection works perfectly, but the update method using the model returns empty (no content at all). I have confirmed that the $data variable does contain the model information as expected as I can print a JSON encode that displays the result I want. It's just not working in response()->json() for some reason.
Very complex code for what it actually does.
Here you have the problem, needless to say to render the response, you need a return.
$user->save();
$this->showOne($user);
}
should be:
$user->save();
return $this->showOne($user);
}
Bonus: I would look into response transformation for future references see Eloquent Resources or Fractal. Instead of doing to much if logic, you can use FormRequest to validate the input.

Return JsonResponse when i use an AuthTokenAuthenticator (symfony 3)

I specify that I start with Symfony. I want to create an API (without FOSRestBundle) with a token as a means of authentication.
I followed different tutorials for this set up. What I would like is when there is an error that is picked up by the class "AuthTokenAuthenticator", it is returned a json and not a html view.
Here are my script:
AuthTokenAuthenticator
namespace AppBundle\Security;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\PreAuthenticatedToken;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationFailureHandlerInterface;
use Symfony\Component\Security\Http\Authentication\SimplePreAuthenticatorInterface;
use Symfony\Component\Security\Http\HttpUtils;
use Symfony\Component\HttpFoundation\JsonResponse;
class AuthTokenAuthenticator implements
SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface
{
const TOKEN_VALIDITY_DURATION = 12 * 3600;
protected $httpUtils;
public function __construct(HttpUtils $httpUtils)
{
$this->httpUtils = $httpUtils;
}
public function createToken(Request $request, $providerKey)
{
//$targetUrlToken = '/auth-tokens'; // login
//$targetUrlUser = '/users/create'; // create account
/*if ($request->getMethod() === "POST" && $this->httpUtils->checkRequestPath($request, $targetUrlUser) || $request->getMethod() === "POST" && $this->httpUtils->checkRequestPath($request, $targetUrlToken) ) {
return;
}*/
$authTokenHeader = $request->headers->get('X-Auth-Token');
if (!$authTokenHeader) {
//return new JsonResponse(array("error" => 1, "desc" => "INVALID_TOKEN", "message" => "X-Auth-Token header is required"));
throw new BadCredentialsException('X-Auth-Token header is required');
}
return new PreAuthenticatedToken(
'anon.',
$authTokenHeader,
$providerKey
);
}
public function authenticateToken(TokenInterface $token, UserProviderInterface $userProvider, $providerKey)
{
if (!$userProvider instanceof AuthTokenUserProvider) {
throw new \InvalidArgumentException(
sprintf(
'The user provider must be an instance of AuthTokenUserProvider (%s was given).',
get_class($userProvider)
)
);
}
$authTokenHeader = $token->getCredentials();
$authToken = $userProvider->getAuthToken($authTokenHeader);
if (!$authToken || !$this->isTokenValid($authToken)) {
throw new BadCredentialsException('Invalid authentication token');
}
$user = $authToken->getUser();
$pre = new PreAuthenticatedToken(
$user,
$authTokenHeader,
$providerKey,
$user->getRoles()
);
$pre->setAuthenticated(true);
return $pre;
}
public function supportsToken(TokenInterface $token, $providerKey)
{
return $token instanceof PreAuthenticatedToken && $token->getProviderKey() === $providerKey;
}
/**
* Vérifie la validité du token
*/
private function isTokenValid($authToken)
{
return (time() - $authToken->getCreatedAt()->getTimestamp()) < self::TOKEN_VALIDITY_DURATION;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
throw $exception;
}
}
Here is the error return that I have when I do not inform the token:
<!DOCTYPE html>
<html>
<head>
<title> X-Auth-Token header is required (500 Internal Server Error)
How can i do to get a return json response ?
If i try to do a return new JsonResponse(array("test" => "KO")) (simple example), i get this error:
<title> Type error: Argument 1 passed to Symfony\Component\Security\Core\Authentication\AuthenticationProviderManager::authenticate() must be an instance of Symfony\Component\Security\Core\Authentication\Token\TokenInterface, instance of Symfony\Component\HttpFoundation\JsonResponse given, called in /Users/mickaelmercier/Desktop/workspace/api_monblocrecettes/vendor/symfony/symfony/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php on line 101 (500 Internal Server Error)
You can create your own Error Handler. It's a event listener or subscriber that listens to kernel.exception, when it adds a response to the event, the event propagation is stopped, so the default error handler will not be triggered.
It could look something like this:
<?php declare(strict_types = 1);
namespace App\EventSubsbscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\KernelEvents;
final class ExceptionToJsonResponseSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::EXCEPTION => 'onKernelException',
];
}
public function onKernelException(GetResponseForExceptionEvent $event): void
{
// Skip if request is not an API-request
$request = $event->getRequest();
if (strpos($request->getPathInfo(), '/api/') !== 0) {
return;
}
$exception = $event->getException();
$error = [
'type' => $this->getErrorTypeFromException($exception),
// Warning! Passing the exception message without checks is insecure.
// This will potentially leak sensitive information.
// Do not use this in production!
'message' => $exception->getMessage(),
];
$response = new JsonResponse($error, $this->getStatusCodeFromException($exception));
$event->setResponse($response);
}
private function getStatusCodeFromException(\Throwable $exception): int
{
if ($exception instanceof HttpException) {
return $exception->getStatusCode();
}
return 500;
}
private function getErrorTypeFromException(\Throwable $exception): string
{
$parts = explode('\\', get_class($exception));
return end($parts);
}
}
ApiPlatform provides its own exception listener like this, that is more advanced, that you could look into if you need "better" exception responses.

Passing Laravel Collection to Vue

I am trying to return customers from model Customer where id < 10. I do have some in the database.
$customers = Customer::where('id', '<', 10)->get();
return json_encode(['customers' => $customers]);
The above code will error out "Trailing data"
and when I try to dd($customers), I get a list of the collection Customer
I have no idea why I keep getting that and why the model is returning raw collection and not an object of the list of customers???
It seems like a null date "updated_at" field is causing the error. don't know why!
Solved By:
on the Customer model I had to do:
//ask Laravel to not manage default date columns
public $timestamps = false;
also I to mutate those columns:
public function setDateRemindedAttribute($value)
{
if (strlen($value)) {
$this->attributes['date_reminded'] = Carbon::createFromFormat('d/m/Y', $value);
} else {
$this->attributes['date_reminded'] = null;
}
}
public function setUpdatedAtAttribute($value)
{
if (strlen($value)) {
$this->attributes['updated_at'] = Carbon::createFromFormat('d/m/Y', $value);
} else {
$this->attributes['updated_at'] = $_SERVER['REQUEST_TIME'];
}
}
Use response()->json() like this
$customers = Customer::where('id', '<', 10)->get();
return response()->json(['customers' => $customers]);

ZF2AuthAcl Module doesnt work out of the box

I picked up this ZF2AuthAcl module to make my life easier. For some reason it does not work out of the box. As soon as i activate it in Zend2 Application.config it takes over the whole site. Meaning it goes straight to login on any page i have. There is a "white list" and i tried to add pages to this in an array and it does not seem to work. I will show the Acl page that it has with the "white list" maybe i did not add them correctly or there is a better way. It is data driven also. Has anyone used this with success or know about it?
The author is the one who told me it probably has to do with the white list.
The area that i added to looked like this:
public function initAcl()
{
$this->roles = $this->_getAllRoles();
$this->resources = $this->_getAllResources();
$this->rolePermission = $this->_getRolePermissions();
// we are not putting these resource & permission in table bcz it is
// common to all user
$this->commonPermission = array(
'ZF2AuthAcl\Controller\Index' => array(
'logout',
'index'
),
);
$this->_addRoles()
->_addResources()
->_addRoleResources();
}
This is the whole thing with parts i added.
namespace ZF2AuthAcl\Utility;
use Zend\Permissions\Acl\Acl as ZendAcl;
use Zend\Permissions\Acl\Role\GenericRole as Role;
use Zend\Permissions\Acl\Resource\GenericResource as Resource;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Acl extends ZendAcl implements ServiceLocatorAwareInterface
{
const DEFAULT_ROLE = 'guest';
protected $_roleTableObject;
protected $serviceLocator;
protected $roles;
protected $permissions;
protected $resources;
protected $rolePermission;
protected $commonPermission;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
public function initAcl()
{
$this->roles = $this->_getAllRoles();
$this->resources = $this->_getAllResources();
$this->rolePermission = $this->_getRolePermissions();
// we are not putting these resource & permission in table bcz it is
// common to all user
$this->commonPermission = array(
'ZF2AuthAcl\Controller\Index' => array(
'logout',
'index'
),
'Frontend\Controller\Index' => array(
'index'
),
'Blog\Controller\Blog' => array(
'blog',
'list',
'view',
'UsMap',
'maps'
)
);
$this->_addRoles()
->_addResources()
->_addRoleResources();
}
public function isAccessAllowed($role, $resource, $permission)
{
if (! $this->hasResource($resource)) {
return false;
}
if ($this->isAllowed($role, $resource, $permission)) {
return true;
}
return false;
}
protected function _addRoles()
{
$this->addRole(new Role(self::DEFAULT_ROLE));
if (! empty($this->roles)) {
foreach ($this->roles as $role) {
$roleName = $role['role_name'];
if (! $this->hasRole($roleName)) {
$this->addRole(new Role($roleName), self::DEFAULT_ROLE);
}
}
}
return $this;
}
protected function _addResources()
{
if (! empty($this->resources)) {
foreach ($this->resources as $resource) {
if (! $this->hasResource($resource['resource_name'])) {
$this->addResource(new Resource($resource['resource_name']));
}
}
}
// add common resources
if (! empty($this->commonPermission)) {
foreach ($this->commonPermission as $resource => $permissions) {
if (! $this->hasResource($resource)) {
$this->addResource(new Resource($resource));
}
}
}
return $this;
}
protected function _addRoleResources()
{
// allow common resource/permission to guest user
if (! empty($this->commonPermission)) {
foreach ($this->commonPermission as $resource => $permissions) {
foreach ($permissions as $permission) {
$this->allow(self::DEFAULT_ROLE, $resource, $permission);
}
}
}
if (! empty($this->rolePermission)) {
foreach ($this->rolePermission as $rolePermissions) {
$this->allow($rolePermissions['role_name'], $rolePermissions['resource_name'], $rolePermissions['permission_name']);
}
}
return $this;
}
protected function _getAllRoles()
{
$roleTable = $this->getServiceLocator()->get("RoleTable");
return $roleTable->getUserRoles();
}
protected function _getAllResources()
{
$resourceTable = $this->getServiceLocator()->get("ResourceTable");
return $resourceTable->getAllResources();
}
protected function _getRolePermissions()
{
$rolePermissionTable = $this->getServiceLocator()->get("RolePermissionTable");
return $rolePermissionTable->getRolePermissions();
}
private function debugAcl($role, $resource, $permission)
{
echo 'Role:-' . $role . '==>' . $resource . '\\' . $permission . '<br/>';
}
}
06/10/2016 Additional information
I have also found that this ACL page is not in any of the pages in the module. The functions are not called out anywhere in any page nor is it "use" on any page. So how is it supposed to work?
Update 06/10/2017 - Area that has been fixed.
I have found where this is used in the module.php there is a whitelist that the pages have to be added too. Below is where you add them.
$whiteList = array(
'Frontend\Controller\Index-index',
*Add whatever modules/controller/action you do not want included*
'ZF2AuthAcl\Controller\Index-index',
'ZF2AuthAcl\Controller\Index-logout'
);
Above is the conclusion of my issue. I stumbled upon it. I did not look in the module.php file. That is where the answer was.
Here is a general implementation of Zend ACL. I followed this one. If you wish you can follow this one too.
Create a file named module.acl.php in the config/ folder of your module. This file contains configuration for roles and permissions. Modify this script as you need.
ModuleName/config/module.acl.php
return array(
'roles' => array(
'guest',
'member'
),
'permissions' => array(
'guest' => array(
// Names of routes for guest role
'users-signup',
'users-login'
),
'member' => array(
// Names of routes for member role
// Add more here if you need
'users-logout'
)
)
);
You need to import the following three classes and define and initialize some methods in the Module.php.
ModuleName/Module.php
use Zend\Permissions\Acl\Acl;
use Zend\Permissions\Acl\Role\GenericRole;
use Zend\Permissions\Acl\Resource\GenericResource;
// Optional; use this for authentication
use Zend\Authentication\AuthenticationService;
Now lets create methods that will deploy ACL and check roles and permissions.
Module::initAcl()
public function initAcl(MvcEvent $e)
{
// Set the ACL
if ($e->getViewModel()->acl == null) {
$acl = new Acl();
} else {
$acl = $e->getViewModel()->acl;
}
// Get the roles and permissions configuration
// You may fetch configuration from database instead.
$aclConfig = include __DIR__ . '/config/module.acl.php';
// Set roles
foreach ($aclConfig['roles'] as $role) {
if (!$acl->hasRole($role)) {
$role = new GenericRole($role);
$acl->addRole($role);
} else {
$role = $acl->getRole($role);
}
// Set resources
if (array_key_exists($role->getRoleId(), $aclConfig['permissions'])) {
foreach ($aclConfig['permissions'][$role->getRoleId()] as $resource) {
if (!$acl->hasResource($resource)) {
$acl->addResource(new GenericResource($resource));
}
// Add role to a specific resource
$acl->allow($role, $resource);
}
}
}
// Assign the fully prepared ACL object
$e->getViewModel()->acl = $acl;
}
Module::checkAcl()
public function checkAcl(MvcEvent $e) {
// Get the route
$route = $e->getRouteMatch()->getMatchedRouteName();
// Use this if you have authentication set
// Otherwise, take this off
$auth = new AuthenticationService();
// Set role as you need
$userRole = 'guest';
// Use this if you have authentication set
// Otherwise, take this off
if ($auth->hasIdentity()) {
$userRole = 'member';
$loggedInUser = $auth->getIdentity();
$e->getViewModel()->loggedInUser = $loggedInUser;
}
// Check if the resource has right permission
if (!$e->getViewModel()->acl->isAllowed($userRole, $route)) {
$response = $e->getResponse();
// Redirect to specific route
$response->getHeaders()->addHeaderLine('Location', $e->getRequest()->getBaseUrl() . '/404');
$response->setStatusCode(404);
return;
}
}
Now call those above methods on the onBootstrap() method in your Module.php. Initialize Module::initAcl() and check resource permission by adding Module::checkAcl() to the route event.
Module::onBootstrap()
public function onBootstrap(MvcEvent $e)
{
$this->initAcl($e);
$e->getApplication()->getEventManager()->attach('route', array($this, 'checkAcl'));
}
Let us know it helps you or not!

Yii2 Dynamic Form update Action is not working

I like to explain my problem clearly,
Am using wbraganca/yii2-dynamicform
Here create action is working perfectly, but in update action
In the code which i marked, i don't know what i need to do, i dont have such field (addresses) in customer table. am stuck on that.
suppose if i create a variable in model like public $addressess, it makes me the reload the table again, and that cause while update the same form, data's getting reload and form viewing as empty without empty,
if create a function on that name, i don't know what to write on that..
Am simply using code like this
public function getaddressess()
{
}
Create Action Code
public function actionCreate()
{
$modelCustomer = new Customer;
$modelsAddress = [new Address];
if ($modelCustomer->load(Yii::$app->request->post())) {
$modelsAddress = Model::createMultiple(Address::classname());
Model::loadMultiple($modelsAddress, Yii::$app->request->post());
// ajax validation
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = Response::FORMAT_JSON;
return ArrayHelper::merge(
ActiveForm::validateMultiple($modelsAddress),
ActiveForm::validate($modelCustomer)
);
}
// validate all models
$valid = $modelCustomer->validate();
$valid = Model::validateMultiple($modelsAddress) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $modelCustomer->save(false)) {
foreach ($modelsAddress as $modelAddress) {
$modelAddress->customer_id = $modelCustomer->id;
if (! ($flag = $modelAddress->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $modelCustomer->id]);
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('create', [
'modelCustomer' => $modelCustomer,
'modelsAddress' => (empty($modelsAddress)) ? [new Address] : $modelsAddress
]);
}
Help me to sort out this problem
$modelsAddress=$modelCustomer->addresses in that example mean array of related Address() instances
public function actionCreate()
{
$modelCustomer = new Customer;
$modelsAddress = $this->getaddressess($modelCustomer->id);
//...................
}
public function getaddressess($id)
{
$model = Address::find()->where(['id' => $id])->all();
return $model;
}
from
public function getaddressess($id)
{
$model = Address::find()->where(['id' => $id])->all();
return $model;
}
Shared above you will also need to add
on your Update view file :
'model' => $model,
'modelsAddress'=>$modelsAddress,
Hope this helps. It worked for me
It should be getAddresses() instead of getaddresses() (although both could work, I'd go with the first one to meet conventions). Or you could set a public $addresses if you don't need extra encapsulation.
suppose if i create a variable in model like public $addressess, it makes me the reload the table again, and that cause while update the same form, data's getting reload and form viewing as empty without empty,
I think you have a validation issue - no validator to mark the field as safe and you see it as empty after posting.
Add public $addresses to your Customer model.
Add "addresses" to your validation rules as safe (or more appropriate validator). This way after posting the form, it probably won't render empty.
This line code ---> $modelsAddress = $modelCustomer->addresses;
is get from model for customer at line ---> public function getAddresses()
this public function line code is code for get array related table from active record method on yii2.
$modelCustomer->addresses the word addresses should come from the $modelCustomer model you must have a relationship to the other table where you add the multiple values. In my example described in the video I have two tables po table and po_items table po_items table has foreign key of po_id. So when you make the Models using gii you will get a relationship in the model that is what you have to use instead of the addresses.
the relationship according my database should be - poItems you will see this at line 14
Add this to Customer Model
public function getAddresses(){
return $this->hasMany(Address::className(), ['id' => 'id']);
}
enter image description hereIn Po.php models:
public function getPoItems()
{
return $this->hasMany(PoItem::className(), ['po_id' => 'id']);
}
In PoController.php
public function actionUpdate($id)
{
$model = $this->findModel($id);
//$modelsPoItem = [new PoItem];
$modelsPoItem = $model->poItems;
if ($model->load(Yii::$app->request->post()) && $model->save())
{
$oldIDs = ArrayHelper::map($modelsPoItem, 'id', 'id');
$modelsPoItem = Model::createMultiple(PoItem::classname(), $modelsPoItem);
Model::loadMultiple($modelsPoItem, Yii::$app->request->post());
$deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsPoItem, 'id', 'id')));
// validate all models
$valid = $model->validate();
$valid = Model::validateMultiple($modelsPoItem) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
if (! empty($deletedIDs))
{
PoItem::deleteAll(['id' => $deletedIDs]);
}
foreach ($modelsPoItem as $modelPoItem)
{
$modelPoItem->po_id = $model->id;
if (! ($flag = $modelPoItem->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $model->id]);
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}
return $this->render('update', [
'model' => $model,
'modelsPoItem' => (empty($modelsPoItem)) ? [new PoItem] : $modelsPoItem
]);
}