Showing posts with label laravel-5. Show all posts
Showing posts with label laravel-5. Show all posts

Wednesday, September 19, 2018

How to group objects based on longitude/latitude proximity using laravel/php

Leave a Comment

I have a group of users. The user count could be 50 or could be 2000. Each should have a long/lat that I have retrieved from Google Geo api.

I need to query them all, and group them by proximity and a certain count. Say the count is 12 and I have 120 users in the group. I want to group people by how close they are (long/lat) to other people. So that I wind up with 10 groups of people who are close in proximity.

I currently have the google geo coding api setup and would prefer to use that.

TIA.

-- Update I have been googling about this for awhile and it appears that I am looking for a spatial query that returns groups by proximity.

3 Answers

Answers 1

Keep in mind that this problem grows exponentially with every user you add, as the amount of distance calculations is linked to the square of the number of users (it's actually N*(N-1) distances... so a 2000 user base would mean almost 4 million distance calculations on every pass. Just keep that in mind when sizing the resources you need

Are you looking to group them based on straight-line (actually great circle) distance or based on walking/driving distance?

If the former, the great circle distance can be approximated with simple math if you're able to tolerate a small margin of error and wish to assume the earth is a sphere. From GCMAP.com:

Earth's hypothetical shape is called the geoid and is approximated by an ellipsoid or an oblate sphereoid. A simpler model is to use a sphere, which is pretty close and makes the math MUCH easier. Assuming a sphere of radius 6371.2 km, convert longitude and latitude to radians (multiply by pi/180) and then use the following formula:

theta = lon2 - lon1 dist = acos(sin(lat1) × sin(lat2) + cos(lat1) × cos(lat2) × cos(theta)) if (dist < 0) dist = dist + pi dist = dist × 6371.2 

The resulting distance is in kilometers.

Now, if you need precise calculations and are willing to spend the CPU cycles needed for much complex math, you can use Vincenty's Formulae, which uses the WGS-84 reference ellipsoid model of the earth which is used for navigation, mapping and whatnot. More info HERE

As to the algorithm itself, you need to build a to-from matrix with the result of each calculation. Each row and column would represent each node. Two simplifications you may consider:

  1. Distance does not depend on direction of travel, so $dist[n][m] == $dist[m][n] (no need to calculate the whole matrix, just half of it)
  2. Distance from a node to itself is always 0, so no need to calculate it, but since you're intending to group by proximity, to avoid a user being grouped with itself, you may want to always force $dist[m][m] to an arbitrarily defined and abnormally large constant ($dist[m][m] = 22000 (miles) for instance. Will work as long as all your users are on the planet)

After making all the calculations, use an array sorting method to find the X closest nodes to each node and there you have it (you may or may not want to prevent a user being grouped on more than one group, but that's just business logic)

Actual code would be a little too much to provide at this time without seeing some of your progress first, but this is basically what you need to do algoritmically.

Answers 2

... it appears that I am looking for a spatial query that returns groups by proximity. ...

You could use hdbscan. Your groups are actually clusters in hdbscan wording. You would need to work with min_cluster_size and min_samples to get your groups right.

https://hdbscan.readthedocs.io/en/latest/parameter_selection.html

https://hdbscan.readthedocs.io/en/latest/

It appears that hdbscan runs under Python.

Here are two links on how to call Python from PHP: Calling Python in PHP, Running a Python script from PHP

Here is some more information on which clustering algorithm to choose: http://nbviewer.jupyter.org/github/scikit-learn-contrib/hdbscan/blob/master/notebooks/Comparing%20Clustering%20Algorithms.ipynb

http://scikit-learn.org/stable/modules/clustering.html#clustering

Answers 3

Use GeoHash algorithm[1]. There is a PHP implementation[2]. You may pre-calculate geohashes with different precision, store them in SQL database alongside lat-lon values and query using native GROUP BY.

  1. https://en.wikipedia.org/wiki/Geohash
  2. https://github.com/lvht/geohash
Read More

Tuesday, September 18, 2018

Make specific folders writeable in laravel coaster cms in google app engine

Leave a Comment

Currently I am using google app engine with laravel custom cms coaster cms. How can I make the following folders writable?

Error on Google App engine

Tried all normal commands, chmod given permission check the above image

permission for the folders

3 Answers

Answers 1

While App Engine runs on VM's, this does not mean that you should rely on changing their permissions. Remember that App Engine is a managed VM, this means that even if you make the folders writable on an instance, if App Engine scales up or has to destroy the instance you modified, you would need to make the changes on the new instances (which you shouldn't be doing).

I would recommend you to use Compute Engine with a managed instance group in order to solve this issue as this would be more practical than to mingle with every instance that spawns for you app.

Answers 2

I am not sure what you need to do to make Coaster CMS folders writable, but you might be able to do whatever you need to do in a dockerfile for your App Engine Flex VMs.

If that's not sufficient, then you can use startup scripts to setup/configure all your GCE VMs at startup.

Answers 3

If the process that the web server runs on is saurabh2836, then you need to add the write permission for the directories listed:

chmod -R u+x public/coaster public/themes public/uploads

Read More

Tuesday, September 4, 2018

Is a Cache mock called more than once when browser-testing?

Leave a Comment

I'm trying to cover the following:

Uncovered line

Resulting in Uncovered Method

I'm using the following test code:

public function test_it_deletes_a_patient() {     // ...      $cacheKey = vsprintf('%s.%s', [$this->doctorUser->id, 'backoffice.stats.patientsTotalCount']);     Cache::shouldReceive('has')->with($cacheKey)->once()->andReturn(false);     Cache::shouldReceive('increment')->with($cacheKey, -1)->once()->andReturn(true);      $response = $this->json('DELETE', route('patients.destroy', $this->patient), ['confirmation' => 'ELIMINAR']);      // ... } 

That triggers the following controller code:

public function destroy(Patient $patient, Request $request) {     $this->authorize('delete', $patient);      $confirmation = $request->get('confirmation');      if ($confirmation != 'ELIMINAR') {         return response()->json(['success' => false]);     }      logger()->info("Deleting Patient Profile PATIENT_ID:[{$patient->id}]");      $patient->delete();      $this->updatePatientsCount(-1);      return response()->json(['success' => true]); }  protected function updatePatientsCount($amount = 1) {     $key = vsprintf('%s.%s', [auth()->user()->id, 'backoffice.stats.patientsTotalCount']);     if (Cache::has($key)) { // I want to mock for testing this         Cache::increment($key, $amount); // I want to mock for testing this     } } 

After test run I get:

alariva@trinsic:~/fimedi$ t --filter=test_it_deletes_a_patient PHPUnit 7.3.1 by Sebastian Bergmann and contributors.  F                                                                   1 / 1 (100%)  Time: 6.53 seconds, Memory: 26.00MB  There was 1 failure:  1) Tests\Browser\Backoffice\PatientsTest::test_it_deletes_a_patient Unable to find JSON fragment ["success":true] within [{"exception":"Mockery\\Exception\\NoMatchingExpectationException","file":"\/home\/alariva\/fimedi\/vendor\/mockery\/mockery\/library\/Mockery\/ExpectationDirector.php","line":92,"message":"No matching handler found for Mockery_0_Illuminate_Cache_CacheManager::has('2056e535e689ab723b3f44831b488f05f7fb8b90'). Either the method was unexpected or its arguments matched no expected argument list for this method\n\n","trace":[{"class":"App\\Http\\Middleware\\Language","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Barryvdh\\Debugbar\\Middleware\\InjectDebugbar","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Auth\\Middleware\\Authenticate","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Cookie\\Middleware\\AddQueuedCookiesToResponse","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Illuminate\\Cookie\\Middleware\\EncryptCookies","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Pipeline\/Pipeline.php","function":"handle","line":151,"type":"->"},{"class":"Il 

What I interpret after a couple of tests, is that it looks like once I mock Cache it is being called by some middlewares before reaching the tested block, so since those called methods are not mocked, the test fails because it does not know what to answer for those middleware calls.

Imagine I could successfully mock all the calls before getting to the tested codeblock, I would be able to make it reach. But that's not the way to go over it.

How can I mock Cache and avoid failure due to previous Cache calls that I'm not testing?

EDIT: I realized after getting to a solution that this is a misleading question. My actual need was:

How can I successfully cover those lines?


Sidenote: if I try to disable middlewares ($this->withoutMiddleware();) I get an AccessDeniedHttpException

alariva@trinsic:~/fimedi$ t --filter=test_it_deletes_a_patient PHPUnit 7.3.1 by Sebastian Bergmann and contributors.  F                                                                   1 / 1 (100%)  Time: 12.95 seconds, Memory: 24.00MB  There was 1 failure:  1) Tests\Browser\Backoffice\PatientsTest::test_it_deletes_a_patient Unable to find JSON fragment ["success":true] within [{"exception":"Symfony\\Component\\HttpKernel\\Exception\\AccessDeniedHttpException","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Foundation\/Exceptions\/Handler.php","line":201,"message":"This action is unauthorized.","trace":[{"class":"App\\Exceptions\\Handler","file":"\/home\/alariva\/fimedi\/vendor\/laravel\/framework\/src\/Illuminate\/Routing\/Pipeline.php","function":"render","line":83,"type":"->"},{"class":"Illuminate\\Foundation\\Exceptions\\Handler","file":"\/home\/alariva\/fimedi\/app\/Exceptions\/Handler.php","function":"render","line":65,"type":"->"},{"class":"Illuminate\\Foundation\\Exceptions\\Handler","file": 

Maybe I can cherry-pick middlewares to disable?

1 Answers

Answers 1

I managed to cover the controller's method by encapsulating the custom Cache operation into a macro, so as to get the benefits of spliting into code units.

  1. I moved my code into a macro (in the boot() of a service provider):

    Cache::macro('incrementExisting', function($key, $amount) {     if (Cache::has($key)) {         Cache::increment($key, $amount);     }     return $this; }); 
  2. I refactored to use the macro

    protected function updatePatientsCount($amount = 1) {     $key = vsprintf('%s.%s', [auth()->user()->id, 'backoffice.stats.patientsTotalCount']);     Cache::incrementExisting($key, $amount); } 

I could get the desired coverage while I can still test the refactored code with unit testing.

Test coverage result

Read More

Friday, August 24, 2018

Laravel : Increase time on second time login attempts

Leave a Comment

Currently five login attempts blocks user for 1 minute and its working fine with the following code :

if ($this->hasTooManyLoginAttempts($request)) {     $this->fireLockoutEvent($request);     return $this->sendLockoutResponse($request); } 

What i want is that, When a user gets unblocked again after the first attempts, On the second attempts the block time should increase to 3 minutes.

I searched around, But could not found anything, Is there any way around it ?

3 Answers

Answers 1

I would suggest you try the following code. Please ask if anything is unclear.

$minutes = 3; $key = $this->throttleKey($request); $rateLimiter = $this->limiter();  if ($this->hasTooManyLoginAttempts($request)) {      $attempts = $rateLimiter->attempts($key);      if ($attempts > 1) {         $attempts === 2 && $rateLimiter->clear($key);         $this->decayMinutes = ($attempts - 1) * $minutes;         $attempts === 2 && $this->incrementLoginAttempts($request);         $this->incrementLoginAttempts($request);     }      $this->fireLockoutEvent($request);     return $this->sendLockoutResponse($request); } 

Code for incremental blocking:

$minutes = 3; $key = $this->throttleKey($request); $rateLimiter = $this->limiter();  if ($this->hasTooManyLoginAttempts($request)) {      $attempts = $rateLimiter->attempts($key);     $rateLimiter->clear($key);     $this->decayMinutes = $attempts === 1 ? 1 : ($attempts - 1) * $minutes;      for ($i = 0; $i < $attempts; $i++) {         $this->incrementLoginAttempts($request);     }      $this->fireLockoutEvent($request);     return $this->sendLockoutResponse($request); } 

Code for incremental blocking with cache:

$minutes = 3; $key = $this->throttleKey($request); $rateLimiter = $this->limiter();  if ($this->hasTooManyLoginAttempts($request)) {      $attempts = $rateLimiter->attempts($key);     $rateLimiter->clear($key);      $reflection = new \ReflectionClass($rateLimiter);     $property = $reflection->getProperty('cache');     $property->setAccessible(true);     $cache = $property->getValue($rateLimiter);      $blockMinutes = $attempts === 1 ? 1 : ($attempts - 1) * $minutes;     $cache->add($key.':timer', $rateLimiter->availableAt($blockMinutes * 60), $blockMinutes);     $added = $cache->add($key, 0, $blockMinutes);     $hits = (int) $cache->increment($key, $attempts);     if (! $added && $hits === 1) {         $cache->put($key, 1, $blockMinutes);     }     $property->setAccessible(false);      $this->fireLockoutEvent($request);     return $this->sendLockoutResponse($request); } 

Answers 2

I think you need to set property in LoginController:

public $decayMinutes = 1; // minutes to lockout 

Also you can controll numbers of attempts:

public $maxAttempts = 5; 

For more information you can investigate: trait AuthenticatesUsers - which has method "login" and code from your description. And this trait uses another trait: "ThrottlesLogins" -> this traits has method named "decayMinutes". It returns number of minutes.

Hope it will help you!

Answers 3

I think laravel default doesn't provide what your need, So you need to save in (cache, session or database) if user had blocked once on your own, and increase decayMinutes as you want.

if ($this->hasTooManyLoginAttempts($request)) {     if(Cache::has($this->throttleKey($request))){         $this->decayMinutes = 3;     }      Cache::put($this->throttleKey($request), true);     $this->fireLockoutEvent($request);      return $this->sendLockoutResponse($request); } 
Read More

Monday, July 30, 2018

Laravel mail with g suites and XOAUTH2

Leave a Comment

I have a g suites account and applications associated with my e-mails. I was looking at the Laravel mail functions but I do not see any option to log in to gmail smtp with xoauth auth type.

I was using PHPMailer with codeigniter and I had to use clientId, clientSecret and refreshToken to send emails via smtp.gmail.com

Is there any chance I can authenticate using xoauth with native laravel swiftmailer?

1 Answers

Answers 1

Since Laravel doesn't have available configuration to set AuthMode then we need to tweak it a little bit.

  1. Register a new Mail service provider in config/app.php:

    // ... 'providers' => [     // ...      // Illuminate\Mail\MailServiceProvider::class,     App\MyMailer\MyMailServiceProvider::class,      // ... 
  2. app/MyMailer/MyMailServiceProvider.php should create your own TransportManager class:

```

namespace App\MyMailer;  class MyMailServiceProvider extends \Illuminate\Mail\MailServiceProvider {     public function registerSwiftTransport()     {         $this->app['swift.transport'] = $this->app->share(function ($app) {              return new MyTransportManager($app);         });     } } 

```

  1. In the app/MyMailer/MyTransportManager.php we can provide additional configuration to the SwiftMailer:

```

<?php  namespace App\MyMailer;   class MyTransportManager extends \Illuminate\Mail\TransportManager {     /**      * Create an instance of the SMTP Swift Transport driver.      *      * @return \Swift_SmtpTransport      */     protected function createSmtpDriver()     {         $transport = parent::createSmtpDriver();         $config = $this->app->make('config')->get('mail');           if (isset($config['authmode'])) {             $transport->setAuthMode($config['authmode']);         }          return $transport;     } } 

```

  1. Last thing to do is to provide mail configuration with authmode set to XOAUTH2 and password to your access token:

```

<?php  return array(  /* |-------------------------------------------------------------------------- | Mail Driver |-------------------------------------------------------------------------- | | Laravel supports both SMTP and PHP's "mail" function as drivers for the | sending of e-mail. You may specify which one you're using throughout | your application here. By default, Laravel is setup for SMTP mail. | | Supported: "smtp", "mail", "sendmail" | */  'driver' => 'smtp',  /* |-------------------------------------------------------------------------- | SMTP Host Address |-------------------------------------------------------------------------- | | Here you may provide the host address of the SMTP server used by your | applications. A default option is provided that is compatible with | the Postmark mail service, which will provide reliable delivery. | */  'host' => 'smtp.gmail.com',  /* |-------------------------------------------------------------------------- | SMTP Host Port |-------------------------------------------------------------------------- | | This is the SMTP port used by your application to delivery e-mails to | users of your application. Like the host we have set this value to | stay compatible with the Postmark e-mail application by default. | */  'port' => 587,  /* |-------------------------------------------------------------------------- | Global "From" Address |-------------------------------------------------------------------------- | | You may wish for all e-mails sent by your application to be sent from | the same address. Here, you may specify a name and address that is | used globally for all e-mails that are sent by your application. | */  'from' => array('address' => 'user@gmail.com', 'name' => 'user'),  /* |-------------------------------------------------------------------------- | E-Mail Encryption Protocol |-------------------------------------------------------------------------- | | Here you may specify the encryption protocol that should be used when | the application send e-mail messages. A sensible default using the | transport layer security protocol should provide great security. | */  'encryption' => 'tls',  /* |-------------------------------------------------------------------------- | SMTP Server Username |-------------------------------------------------------------------------- | | If your SMTP server requires a username for authentication, you should | set it here. This will get used to authenticate with your server on | connection. You may also set the "password" value below this one. | */  'username' => 'user@gmail.com',  /* |-------------------------------------------------------------------------- | SMTP Server Password |-------------------------------------------------------------------------- | | Here you may set the password required by your SMTP server to send out | messages from your application. This will be given to the server on | connection so that the application will be able to send messages. | */  'password' => 'YOUR ACCESS TOKEN',  /* |-------------------------------------------------------------------------- | Sendmail System Path |-------------------------------------------------------------------------- | | When using the "sendmail" driver to send e-mails, we will need to know | the path to where Sendmail lives on this server. A default path has | been provided here, which will work well on most of your systems. | */  'sendmail' => '/usr/sbin/sendmail -bs',  /* |-------------------------------------------------------------------------- | Mail "Pretend" |-------------------------------------------------------------------------- | | When this option is enabled, e-mail will not actually be sent over the | web and will instead be written to your application's logs files so | you may inspect the message. This is great for local development. | */  'pretend' => false,  'authmode' => 'XOAUTH2',  ); 

```

Read More

Wednesday, June 20, 2018

Is there another way to “setConnection” on an Eloquent Model?

Leave a Comment

I am currently handling a "multi db on the fly swap connections" sort of project.

So what I end up doing is the following:

$connectionName = uniqid(); \Config::set('database.connections.' . $connectionName, [/** db options **/]); \Artisan::call('migrate', ['--database' => $connectionName]); 

or

$connectionName = uniqid();            \Config::set('database.connections.' . $connectionName,[/** db options **/]);  $user = new User(); $user->setConnection($connectionName); $user->first_name = 'Daisy'; $user->last_name = 'Demo'; $user->is_not_being_ignored_by_santa_this_year = 0; $user->email = //and so so on $user->save(); 

For the Artisan call I sort of understand why Laravel needs to refer to a connection in a string, saved in a config array.

However on the Eloquent Model itself I find it somehow cumbersome to have to write my DB connection into a config array. So it can be picked up by the "Singleton approach" \Config::get().. in the Model.

Is there something more elegant, where I can inject a configuration directly without having to write it into some super global ?

Or am I missing something ?

4 Answers

Answers 1

You would probably be better off creating a configuration array for each connection then you could switch between connections pretty easily by specifying which connection to use.

If you need to use multiple connections on the same model you can use the on method:

so it would be something like User::on('dbconnection2')->find(1)

If you just want to use different connections for different models, you can set the protected $connection property on the model:

class User extends Model {     protected $connection = 'dbconnection2'; } 

Hope that helps.

Answers 2

You could create a factory for your models and pass it the connection on bootstrapping your app:

<?php  class ModelFactory {     private $db;      public function __construct($dbConnection)     {         $this->db = $dbConnection;     }      public function createNewModel($class)     {         $object = new $class();         $object->setConnection($this->db);         return $object;     } } 

Then in your code:

$user = $factory->createModel(User::class); 

Something like this! Good luck! :-)

Answers 3

Its all depends on how you handling your multiple connections.I have worked on similar requirement project.

  • we have master slave/tenant database connection. with configuration https://gist.github.com/safoorsafdar/c6c623f9ec7b440f563d76995faf7aec#file-database-php
  • tenant database/migration/seeds create on the fly with create new account in the system
  • can soft delete database from the root control.
  • on the user login, we check if its master, connect to master connection otherwise get tenant connection, store information in the session for web access. check Tenant Setup after login.
  • Database connection related operation central class. View DatabaseConnection.
  • center tenant session handler class to help with resolve tenant id from the session. View TenantContextSession
  • we have separate models with abstract class for tenant and master to get set the model connection. Check
  • You might also need to handle migration in your system, have look into this https://gist.github.com/safoorsafdar/cc9252a2f14301c3da942ca7bec7d66e

Tenant Model Abstract Class

<?php  namespace App\Models\Abstracts;  use App\Models\Abstracts\AbstractBaseModel; use App\Models\Picklist\PicklistValue;  class TenantAbstractBaseModel extends AbstractBaseModel {     function __construct(array $attributes = array())     {         parent::__construct($attributes);         if ( ! is_null(app('tenant.context')->getConnectionName())) {             $this->setConnection(app('tenant.context')->getConnectionName());         }          //todo; should be dynamic         if (is_null(app('tenant.context')->getConnectionName())             && app()->runningInConsole()         ) {             //todo; need to resolve database connection through terminal and application.             //dd(config('tenant.tenant_connection'));             //$this->setConnection(config('tenant.tenant_connection'));         }      } } 

Tenant Setup after login

$connection = config('tenant.tenant_connection');                 //config()->set('database.default', config('tenant.tenant_connection'));             app('tenant.context')->setConnectionName($connection);             app('tenant.context')->setTenantId($company_id);               //$database = config('database.connections.' . $connection . '.database') . $company_id;              $company_system_name                     = $this->auth->user()->company->system_name;              config()->set('database.connections.'.$connection.'.database',                     $company_system_name);              //config()->set('database.connections.' . $connection . '.database', $database);            config()->set('database.default', $connection); 

DatabaseConnection

<?php  namespace App\Tenancy\Tenant;  use Config; use DB; use App\Tenancy\Exceptions\TenantDatabaseException; use App\Tenancy\Models\Tenant;  /**  * Class DatabaseConnection  *  * Helps with tenant database connections  */ class DatabaseConnection {     /**      * See the multi-tenant configuration file. Configuration set      * to use separate databases.      */     const TENANT_MODE_SEPARATE_DATABASE = 'database';      /**      * See the multi-tenant configuration file. Configuration set      * to use prefixed table in same database.      */     const TENANT_MODE_TABLE_PREFIX = 'prefix';     /**      * Current active global tenant connection.      *      * @var string      */     protected static $current;     /**      * @var string      */     public $name;     /**      * @var Tenant      */     protected $tenant;     /**      * @var \Illuminate\Database\Connection      */     protected $connection;      public function __construct(Tenant $tenant)     {         $this->tenant = $tenant;          $this->name = "tenant.{$this->tenant->hash_id}";          $this->setup();     }      /**      * Sets the tenant database connection.      */     public function setup()     {         Config::set("database.connections.{$this->name}", $this->config());     }      /**      * Generic configuration for tenant.      *      * @return array      */     protected function config()     {         $clone             = Config::get(sprintf('database.connections.%s',             static::tenantConnectionName()));         $clone['database'] = $this->tenant->system_name;          return $clone;     }      /**      * Central getter for system connection name.      *      * @return string      */     public static function systemConnectionName()     {         return Config::get('tenant.master_connection', 'mysql');     }      /**      * Checks whether current connection is set as global tenant connection.      *      * @return bool      */     public function isCurrent()     {         return $this->name === static::getCurrent();     }      /**      * Loads the currently set global tenant connection name.      *      * @return string      */     public static function getCurrent()     {         return static::$current;     }      /**      * Sets current global tenant connection.      */     public function setCurrent()     {         static::$current = $this->name;         Config::set(sprintf('database.connections.%s',             static::tenantConnectionName()), $this->config());          DB::purge(static::tenantConnectionName());     }      /**      * Central getter for tenant connection name.      *      * @return string      */     public static function tenantConnectionName()     {         return Config::get('tenant.tenant_connection', 'tenant_mysql');     }      /**      * Loads connection for this database.      *      * @return \Illuminate\Database\Connection      */     public function get()     {         if (is_null($this->connection)) {             $this->setup();             $this->connection = DB::connection($this->name);         }          return $this->connection;     }      /**      * @return bool      */     public function create()     {         $clone = $this->config();          return DB::connection(static::systemConnectionName())             ->transaction(function () use ($clone) {                 if ( ! DB::connection(static::systemConnectionName())                     ->statement("create database if not exists `{$clone['database']}`")                 ) {                     throw new TenantDatabaseException("Could not create database {$clone['database']}");                 }                 if ( ! DB::connection(static::systemConnectionName())                     ->statement("grant all on `{$clone['database']}`.* to `{$clone['username']}`@'{$clone['host']}' identified by '{$clone['password']}'")                 ) {                     throw new TenantDatabaseException("Could not create or grant privileges to user {$clone['username']} for {$clone['database']}");                 }                  return true;             });     }      /**      * @throws \Exception      *      * @return bool      */     public function delete()     {         $clone = $this->config();          return DB::connection(static::systemConnectionName())             ->transaction(function () use ($clone) {                 if ( ! DB::connection(static::systemConnectionName())                     ->statement("revoke all on `{$clone['database']}`.* from `{$clone['username']}`@'{$clone['host']}'")                 ) {                     throw new TenantDatabaseException("Could not revoke privileges to user {$clone['username']} for {$clone['database']}");                 }                 if ( ! DB::connection(static::systemConnectionName())                     ->statement("drop database `{$clone['database']}`")                 ) {                     throw new TenantDatabaseException("Could not drop database {$clone['database']}");                 }                  return true;             });     } } 

*TenantContextSession *

<?php  namespace App\Repositories\Tenant;  use App\Repositories\Tenant\TenantContextRepositoryContract;  /**  * Description of TenantContextSession  *  * @author safoor  */ class TenantContextSession implements TenantContextRepositoryContract { //    public function __construct() { //        $this->clearTenantSession(); //    }      /**      * Sets the connection name for the actual context      * this tenant      * @param $name      * @return mixed      */     public function setConnectionName($name)     {         if (session()->has('tenant_connection')) {             session()->set('tenant_connection', '');         }          session()->put('tenant_connection', $name);     }      /**      * Get the name of the current connection in context      * @return mixed      */     public function getConnectionName()     {         return session()->get('tenant_connection');     }      /**      * Sets the id value filter data in the current context      * @param $id      * @return mixed      */     public function setTenantId($id)     {         if (session()->has('tenant_id')) {             session()->set('tenant_id', '');         }         session()->put('tenant_id', $id);     }      /**      *      * @return mixed      */     public function getTenantId()     {         return session()->get('tenant_id');     }      public function clearTenantSession()     {         session()->flush();         session()->set('tenant_id', '');         session()->set('tenant_connection', '');     } } 

I hope this will help to work with your scenario, I have tried to mention all of aspect we had to covered, but if still there is something confusing and need explanation from my side please let me know.

Answers 4

I build a multitenant laravel app and was surprised that there is no out-of-the-box way of doing that.

I have one app available via different subdomains and the subdomain should be the key to different configs, like the database connection.

You can easily adapt that to whatever criteria you need instead of the subdomain.

Via dynamically Config::set()

So my first attempt was to always use the "default" connection and to create a middleware that dynamically calls Config::set("database.connection.default.host", "1.3.5.7"); and so on for all the other settings.

But there are some downsides. For example it was pretty slow because I read all the values from the database and later on from redis. But the much bigger problem was to set the connection to the redis cache for example, because the redis connection is already established before the middleware it called to override the config setting.

Via own config files

So my second and current approach is to make it all via config files. So I created the following file structure:

  • config/_myapp.php
  • config/subdomain1/_myapp.php

Note: The underscore is important because the files are read in alphabetic order and our new file has to be read at first to use it in other config files.

The goal is to use config('_myapp.DB_HOST') to retrieve the DB_HOST value from config/[subdomain]/_myapp.php and use that as value in config/database.php. So config/_myapp.php only returns the content of _myapp.php for the specific subdomain.

in config/_myapp.php:

// get the current subdomain from $_SERVER['HTTP_HOST'] $subdomain = \App\Helper::getSubDomain();  define('SUBDOMAIN', $subdomain);  if(!empty($subdomain) && file_exists($file = __DIR__."/$subdomain/".basename(__FILE__)))     return require($file);  return [  ]; 

and in config/_myapp.php:

return [     ...     'DB_HOST' => '1.3.5.7',     ... ]; 

Use this for example in config/database.php or whereever you need domain specific config:

...    'host' => config('_myapp.DB_HOST'), ... 

Please ask if something doesn't work, took me quite some time to figure that stuff out.

Read More

Saturday, June 16, 2018

Laravel 5.5 and CKEditor Options

Leave a Comment

I want to use some of the optional extras, specifically video embedding, in CKEditor.

I have downloaded the entire thing to ckeditor in the public area, and in the plugins directory there is the video.

I start with the CDN of CKeditor:

<script src="//cdn.ckeditor.com/4.7.3/full-all/ckeditor.js"></script> 

and then I add the option for the video plugin:

<script>  CKEDITOR.plugins.addExternal( 'video', '{{ public_path('\ckeditor\plugins\video\ ') }}', 'video.js' ); </script> 

(The video.js actually is in a subdirectory dialogs which I have tried as well).

I can see the CKEditor which appears on my page but no video button.

Anyone any ideas please?

1 Answers

Answers 1

First of all, you need to upload the contents of the plugin archive to any folder on your website. Although, it is a good idea to name the folder so that you knew it holds CKEditor plugins. Let’s name it as ckeditor/plugins for the sake of our example. You should end up with the following path then:

ckeditor/plugins/jsplus_image_editor 

Now, we need to tell CKEditor to load the plugin from the above folder. Add the following code to your HTML code above the line where CKEditor replaces the standard control:

<textarea name="editor1"></textarea> ... <script> CKEDITOR.plugins.addExternal( 'yourpluginname',  '/ckeditor/plugins/yourpluginname', 'plugin.js' ); CKEDITOR.replace('editor1'); ... </script> 

Normally you install plugins trough the config.js but since you are using a cdn we need to replace the config. Update the above replace with the following code:

CKEDITOR.replace('editor1', { customConfig: '/ckeditor/custom_config.js'}); 

make the above mentioned custom_config.js and place the following code CKEDITOR.editorConfig = function( config ) {

CKEDITOR.editorConfig = function( config ) {  config.language = 'en';  config.extraPlugins = 'PLUGINNAME';  config.toolbar = 'custom'; config.toolbar_custom = [     { name: 'clipboard', groups: [ 'clipboard', 'undo' ], items: [ 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo' ] },     { name: 'editing', groups: [ 'find', 'selection', 'spellchecker' ], items: [ 'Scayt' ] },     { name: 'links', items: [ 'Link', 'Unlink', 'Anchor' ] },     { name: 'insert', items: [ 'Image', 'Table', 'HorizontalRule', 'SpecialChar' ] },     { name: 'tools', items: [ 'Maximize' ] },     { name: 'document', groups: [ 'mode', 'document', 'doctools' ], items: [ 'Source' ] },     { name: 'others', items: [ '-' ] },     '/',     { name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ], items: [ 'Bold', 'Italic', 'Strike', '-', 'RemoveFormat' ] },     { name: 'paragraph', groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ], items: [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote' ] },     { name: 'styles', items: [ 'Styles', 'Format' ] },     { name: 'about', items: [ 'About' ] },     { name : 'new_group', items: ['PLUGINNAME'] } ];} 

hope this helps!

Read More

Saturday, June 2, 2018

Laravel Botman Studio

Leave a Comment

I've installed botman studio on my existing laravel project to create a chat bot. The chatbot works. However, im looking for complex conversations where user can ask chatbot "What is Example" and Chat box searches from the database table and answers back.

I do not find any tutorial or links that can help me get started. Does anyone know how to do this? A simple example could help me

1 Answers

Answers 1

This would be a possible implementation to tipical questions where you listen for the last keyword, so you could configure bot to listen What is + *keyword_to_search*

$botman->hears('What is (^[a-zA-Z0-9_]*$)', function ($bot, $keyword) {     $answer = \App\Answer::where('keyword', 'LIKE', $keyword)->get();     $bot->reply('Answer: '.$answer); }); 

This is extremly simple, you could start chat with --help and list all keywoards that a user can ask for example, it would be kinda cool I guess.

If you would like to implement more complex stuff you could try to learn about Natural Language Processing(NPL), you can read more about it on botman docs.

Read More

Thursday, May 31, 2018

Diagram of laravel architecture?

Leave a Comment

Can anyone point me to a diagram that shows the relationship between the normal MVC bits and the following:

  • middleware
  • Guards
  • facades
  • Contracts

Laravel seems to have so many middlemen and I'm struggling to see the big picture.

0 Answers

Read More

Friday, April 27, 2018

When calling DB::select why do I get a “The connection was reset” message?

Leave a Comment

In my Laravel 5.5 application, calls to DB::select which run a select query on a Postgresql database fail without showing any error in the Apache or Laravel error logs and trigger a "The connection was reset" message. This code sample runs as expected because the function get_users_with_roles exists.

public function missing_function(Request $request) {         try{            $all = DB::select('SELECT * from get_users_with_roles()', []);         }catch(Illuminate\Database\QueryException $qe){             return json_encode($qe->getMessage());         }         return json_encode($all); } 

However, if I replace that SQL string with a function that doesn't exist:

public function missing_function(Request $request) {         try{            $all = DB::select('SELECT * from test()', []);         }catch(Illuminate\Database\QueryException $qe){             return json_encode($qe->getMessage());         }         return json_encode($all); } 

The connection is reset and I can't see any errors in the logs. If I run this erroneous query in a native Postgresql environment:

SELECT * from test(); 

I get a clear error message:

    ERROR:  function test() does not exist LINE 1: select * from test()                       ^ HINT:  No function matches the given name and argument types. You might need to add explicit type casts. 

It is particularly strange because this problem is not consistent. The try block sometimes catches the QueryException and displays the Postgresql error message as excepted.

I have tried adding

php_flag xcache.cacher Off  php_flag xcache.size 0  php_flag xcache.stat Off 

to the .htaccess file but to no avail.

I need the ability to use the DB::select method because I rely heavily on Postgresql user-defined SQL and plpgsql functions in the application. I have a function which constructs the relevant SQL and passes it the DB::select method programmatically, so I need to be able to catch exceptions thrown when there is an error in the SQL, such as when the function is missing.

UPDATE

This problem seems to be with the way DB::select handles any SQL error. I've just tried this out with a function which exists but which throws an SQL error. Again, instead of allowing me to catch this in PHP with a try/catch block, it just resets the connection and doesn't log an error in either the Laravel log or the Apache log.

This question doesn't shed any light. The accepted answer there refers to the expected behaviour. In my environment, the QueryException isn't thrown or caught.

2 Answers

Answers 1

The tricky part of this has been the browser's stubborn refusal to reveal any form of error message. When that happens, I like to go to the command line and try it, thus eliminating the web server as a variable.

From chat, we learned that the command line showed the error as expected, but did not gracefully do so: the error was output, and the script was halted. That's a hard crash, one not attributable to the web server.

With the introduction of \Throwable, the scenarios where PHP dies hard are becoming fewer and farther between. So, in an effort to catch PHP's dying breath, we implemented a register_shutdown_function that pulled error_get_last in an effort to figure out what, if anything, was said just before blowing up.

This revealed, briefly, the error message in the browser (this time using a different browser). However, this was not repeatable. The insight at this point was caching: composer dump-autoload fixed the problem!

I suspect what happened is this:

  • Eloquent threw an exception
  • PHP was bubbling that up through Laravel's exception handling classes
  • At some point, PHP attempted to load a class that wasn't in the autoloader
  • PHP crashed hard (this is one of those cases where PHP 7.0 bails)

By running composer dump-autoload, all the "missing" classes were brought into the autoloader's purview and, when tried again, the correct code sequence happened.

Answers 2

i think its an Sql query error

 `SELECT * from test()`  

Since () bracket indicates the function so try to use like

 `SELECT * from test` in your query  

Best way in laravel

Create a model with php artisan make:model Test

Then use in controller like

     `use App\Test;' 

and then to fetch records Test::all(); it will bring all records from database like your requirement SELECT * from Test

Read More

Thursday, March 8, 2018

Instagram Integration on Laravel 5

Leave a Comment

I kept getting this issue after installing this package below

https://github.com/vinkla/instagram

into my Laravel 5.1 project.

2018-02-27 at 1 55 36 pm

I followed everything in the instruction.

I am on Mac OS X, PHP 7.1, Laravel 5.1

Did I forget something?

How would one go about and debug this further ?


I'm open to any suggestions at this moment.

Any hints/suggestions / helps on this be will be much appreciated!

3 Answers

Answers 1

Your report() method is being passed a PHP7 Throwable instead of an Exception.

Laravel 5.1 was not updated to support PHP7 Throwables until 5.1.8.

Considering the error, and the line number specified in HandleExceptions.php, it seems as if you are using a version previous to this (5.1.0 - 5.1.7).

You will need to update Laravel to at least 5.1.8 to fix this error. 5.1.8 was updated to convert Throwables to Symfony\Component\Debug\Exception\FatalThrowableError exceptions, which are then passed to the report() method.

Answers 2

You could change app\Exceptions\Handler.php to not have the type declaration Exception and handle some logic within it to convert the Error to an Exception. It looks like this is a known issue in laravel 5.2 <= with php 7. https://github.com/laravel/framework/issues/9650

from:

/**  * Report or log an exception.  *  * This is a great spot to send exceptions to Sentry, Bugsnag, etc.  *  * @param  \Exception  $exception  * @return void  */ public function report(Exception $exception) {     parent::report($exception); } 

to:

/**  * Report or log an exception.  *  * This is a great spot to send exceptions to Sentry, Bugsnag, etc.  *  * @param  \Exception  $exception  * @return void  */ public function report($exception) {     if ($exception instanceof Exception) {         parent::report($exception);     } else {        // convert to exception and then parent::report.     }  } 

You will most likely need to do the same thing with the Handler render method.

Answers 3

It seems to be a bug in Laravel. Do you have the last release of Laravel 5.1?

For helping debug, you might go to the vendor/Illuminate/Foundation/Bootstrap/HandleExceptions@handleException and add dd($e) at the first line of method.

Ex:

public function handleException($e) {     dd($e);     //.. } 
Read More

Wednesday, January 31, 2018

PDF as blank page in HTML

Leave a Comment

My problem is, everything is fine opening PDFs using my browsers, until I uploaded a pdf with a form inside. Then, if I embed it, it returns a blank page. But the other pdfs with forms open normally. Please see my code below:

<object data="{{ asset($test->file_path) }}" type="application/pdf" width="100%" height="100%">     <embed src="{{ asset($test->file_path) }}" type='application/pdf'>     <center>         <a href="{{ route('download.test', ['id' => $test->id]) }}" class="btn btn-primary">Please click here to view</a>     </center> </object> 

Note: I've also tried to use <iframe> but still returns blank page.

4 Answers

Answers 1

<a href="{{ route('download.test', ['id' => $test->id] ,['target'=>'_blank']) }}" class="btn btn-primary">Please click here to view</a> 

Answers 2

It's late, and I'm tired, so apologies if I misread the question.

I noticed that the PDF is hosted on a site that doesn't support HTTPS. It showed a blank page if it was embedded on a site using HTTPS, but worked fine when it was using HTTP.

I think you need to either move the PDF to a site that supports HTTPS or make the site hosting the PDF start using HTTPS.

Answers 3

Consider using Objects and Iframes (Rather than Object and Embed)

Something like this should work for you:

<object data="http://foersom.com/net/HowTo/data/OoPdfFormExample.pdf" type="application/pdf" width="100%" height="100%">     <iframe src="http://foersom.com/net/HowTo/data/OoPdfFormExample.pdf" width="100%" height="100%" style="border: none;">         This browser does not support PDFs. Please download the PDF to view it: <a href="/pdf/example.pdf">Download PDF</a>     </iframe> </object> 

This worked when I tested it locally but I can't show JSFiddle since it uses HTTPS. Also, have a look at these examples: https://pdfobject.com/static.html

Answers 4

Not sure if this will work as I am not able to test your case. You can try this, it always works for me. Try replacing http://yoursite.com/the.pdf with the correct path.

<object data="http://yoursite.com/the.pdf" type="application/pdf" width="750px" height="750px">     <embed src="http://yoursite.com/the.pdf" type="application/pdf">         <p>This browser does not support PDFs. Please download the PDF to view it: <a href="http://yoursite.com/the.pdf">Download PDF</a>.</p>     </embed> </object> 
Read More

Tuesday, January 23, 2018

How to store a file on the local storage system

Leave a Comment

I'm trying to upload a file to a temporary location with Laravel Storage Facade and its saying that the file doesn't exist. I'm trying to put the file into ./storage/app/roofing/projects/{id}/then the file. Not sure what I'm doing wrong here.

foreach ($files as $file) {     $path = Storage::putFile('contract-assets', new File('../storage/app/roofing/projects/'. $project->id.'/'. $file)); } 

I am having to send the file after being stored on my local server to AWS S3 because of the time it takes to try and upload straight to S3 and with my case it times due to the amount of files needing to be stored. As of right now all I get back is true in my dd(). What could be the casue of this.

if (!empty($request->contract_asset)) {     $files = $request->file('contract_asset');     foreach ($files as $file) {         Storage::putFile(Carbon::now()->toDateString().'/roofing/projects/'. $project->id.'/contract-assets', $file);     } }  foreach (Storage::files(Carbon::now()->toDateString().'/roofing/projects/'.$project->id.'/contract-assets') as $file) {     dispatch(new ProcessRoofingProjectContractAssets($file, $project)); } 

My Job file.

/**  * Execute the job.  *  * @return void  */ public function handle() {     $path = Storage::disk('s3')->put('contract-assets', $this->asset, 'public');     dd($path);     $this->project->addContractAsset($path);     } 

UPDATE:

This is my current changes and I am receiving the following error message. Failed because Unable to JSON encode payload. Error code: 5

foreach ($files as $file) {     $returnedStoredFile = Storage::putFile('/roofing/projects/' . $project->id . '/contract-assets/'.Carbon::now()->toDateString(), $file);     dispatch(new ProcessRoofingProjectContractAssets(Storage::get($returnedStoredFile), $project)); } 

1 Answers

Answers 1

You shouldn't be passing an entire file as parameter to your Job. Laravel will serialize it, and it would be extremely memory inefficient. (It's probably during this serialization that you are having this issue now - Failed because Unable to JSON encode payload. Error code: 5)

I'm assuming that you're not having problems to upload in the temporary folder, and you are having problems to dispatch the job (or executing the job).

Edit your job to receive a file path as parameter. Only within the execution of the job you'll go to disk and read it.

Dispatching the job:

foreach ($files as $file) {     $returnedStoredFile = Storage::putFile('/roofing/projects/' . $project->id . '/contract-assets/'.Carbon::now()->toDateString(), $file);     dispatch(new ProcessRoofingProjectContractAssets($returnedStoredFile, $project)); } 

And executing it:

/**  * Execute the job.  *  * @return void  */ public function handle() {     $file = Storage::get($this->asset);     $path = Storage::disk('s3')->put('contract-assets', $file, 'public');     dd($path);     $this->project->addContractAsset($path);     } 
Read More

Sunday, January 14, 2018

modify back route in laravel

Leave a Comment

In my scenario, I am coming from the following route:

model/{model}/edit 

and I am accessing route:

model/{model}/duplicate 

I make a copy of the model and store it

$duplicate_model = $model->replicate(); $duplicate_model->save(); 

after that I wish to return back to edit route of the new model by doing something along the lines of this:

return redirect()->back()->with('model' => $duplicate_model); 

hoping that it would replace the model id with that of the duplicated model, but it does not.

I cannot access a specific route, because there are different cases in which the duplicate route may be accessed.

2 Answers

Answers 1

One of the solutions is to get resolve to resolve a route name from the back URL like this, provided that all possible back routes are named:

$back_route_name = app('router')->getRoutes()->match(app('request')->create(redirect()->back()->getTargetUrl()))->getName(); 

and then redirect to the route by name:

return redirect()->route($back_route_name, ['template' => $duplicate_template]); 

Answers 2

You can define a "path()" in your model. In this case /

/model/{model}/edit

define a function in your model

public function path() { return '/model/' . $this->id . '/edit'; }

in your ThatModelController.php

simply

return redirect($dumplicate_model->path());

It should work,Jeffery Way uses this convention.

Note: If you are using route model binding and using slug the path() function should return $this->slug instead of $this->id

Read More

Monday, January 8, 2018

Laravel Modal Factories with Tests

Leave a Comment

I'm trying to create a team and then add that team to a game and then add that game to the event, however, when the game is created it auto generates an event to attach to the event. In normal circumstances this is fine but because I'm testing the team's joined at compared to the event their first game is on then I need to create an event with a specific date. I can't create the event first because the game has to be created first to be able to add it to it.

Does anyone have a suggestion on correcting my logic so that I can get a game and event created correctly?

I don't know what I should need to do for this edge case.

/** @test */ public function a_team_with_a_game_after_they_started_the_season_cannot_have_their_joined_at_date_after_their_first_match() {     $team = factory(Team::class)->create(['joined_at' => '2017-10-08']);      $game = GameFactory::create([], [$team]);     $event = EventFactory::create(['date' => '2017-10-09'], null, $game);      $validator = new BeforeFirstGameDate($team);      $this->assertFalse($validator->passes('joined_at', '2017-10-10'));     $this->assertEquals('The joined at date cannot be AFTER the team\'s first game.', $validator->message()); }  Factories  <?php  use App\Models\Game; use App\Models\Team;  class GameFactory {     public static function create($overrides = [], $teams = [])     {         $match = factory(Game::class)->create($overrides);          self::addTeamsForGame($teams, $game);          return $game;     }       /**      * @param $teams      * @param $game      */     public static function addTeamsForGame($teams, $game)     {         $teamsForGame = [];          $numberOfTeamsToAdd = $numberOfTeams - count($teams);          if ($numberOfTeamsToAdd) {             $teamsForMatch = factory(Team::class, $numberOfTeamsToAdd)->create();             array_push($teams, $teamsForGame);         } else {             array_push($teams, $teamsForGame);         }          $match->addTeams($teamsForGame);     } }   <?php  use App\Models\Event;  class EventFactory {     public static function create($overrides = [], $totalNumberOfGames = 8, $games = [])     {         $event = factory(Event::class)->create($overrides);          $numberOfGamesToAdd = $totalNumberOfGames - count($games);         $gameToStartAt = count($games) + 1;          foreach (array_wrap($games) as $game) {             $game->addToEvent($event);         }          for ($gameNumber = $gameToStartAt; $gameNumber <= $numberOfGamesToAdd; $gameNumber++) {             GameFactory::create(['event_id' => $event->id, 'game_number' => $gameNumber]);         }          return $event;     } }   $factory->define(App\Models\Game::class, function (Faker\Generator $faker) {     static $order = 1;     return [         'event_id' => function () {             return factory(App\Models\Event::class)->create()->id;         },         'game_number' => $order++,      ];   });  $factory->define(App\Models\Event::class, function (Faker\Generator $faker) {     $name = $faker->sentence;     return [         'name' => $name,         'slug' => str_slug($name),         'date' => $faker->dateTimeBetween('-10 years'),     ];  }); 

2 Answers

Answers 1

Note: The source code shown below is based on some assumptions since the specific implementation of models like for instance Event, Game and Team were not given in the original question. Consider adding a Git repository containing the sources to the question to get more specific answers that reflects your implementation in more detail.

First, some general remarks to tests:

  • unit tests (in case the code show above is one, with the focus on unit) should only test one specific aspect of your domain layer - thus, to ensure that BeforeFirstGameDate is behaving correctly but not testing a combination of possible involved services (database) or factories for object reconstitution - the techniques to be used are "mocks" or "prophecies"
  • integration tests (that's how the code above looks like) should on the other hand be tested with the real implementation (no "mocks" nor "prophecies" if possible), but on a faked/provided scenario - means if you want to test the whole application of factories, models and validators you should provide a complete testing scenario in the database for instance and perform your tests based on these

That being said and putting the focus more on the unit part in "unit test" your tests might look like - focussing on testing single, separated units and not a combination of them:

/** @test */ public function eventIsCreated() {   ...   static::assertSame($expectedEvent, EventFactory::create(...)); } /** @test */ public function teamIsCreated() {   ...   static::assertSame($expectedTeam, TeamFactory::create(...)); } /** @test */ public function gameIsCreated() {   ...   static::assertSame($expectedGame, GameFactory::create(...)); } /** @test */ public function beforeFirstGameDateValidatorRejectsLateApplications() {   ... } 

The mentioned test case for BeforeFirstGameDate validation might just look like the following then, using prophecies - the instance to be tested is named as $subject to make it clear, what's the subject to be tested (as common best practice in writing tests):

/**  * @test  */ public function beforeFirstGameDateValidatorRejectsLateApplications() {     $event = $this->prophesize(Event::class);     $game = $this->prophesize(Game::class);     $team = $this->prophesize(Team::class);      $event->getGames()->willReturn([$game->reveal()]);     $game->getTeams()->willReturn([$team->reveal()]);     $team->get('joined_at')->willReturn('2017-10-08');      $subject = new BeforeFirstGameDate($team->reveal());      static::assertFalse(         $subject->passes('joined_at', '2017-10-10')     ); } 

This way, your Event, Game and Team models don't rely on any factory implementation anymore, but simulate properties and behavior using prophecies. Thus, in case factories get changed or refactored you only have to adjust those tests that assert object reconsitution - the mentioned beforeFirstGameDateValidatorRejectsLateApplications can be skipped since it does not have a hard dependency on these factories for instance.

As mentioned in the beginning the methods and properties for Event, Game and Team are just assumptions since the real implementation was unknown at the time of writing this answer.

References:

Answers 2

You should use the same logic as when you are doing this in your application :

• Create an event

• Add a game to the event

• Add a team to the game

If you need to test your date, then just edit them after :)

Read More

Saturday, October 28, 2017

ReflectionException thrown on composer update for a file that exists

Leave a Comment

To make this more interesting, things work just fine if I run composer dump-autoload -o but I am curious why would this throw an error when I run composer update in the first place? I need to get to the bottom of this. A quick fix doesn't make me happy internally.

aligajani at Alis-MBP in ~/Projects/saveeo on master ✗                                                                                    [faaba41c]  4:53 > composer update > php artisan clear-compiled Loading composer repositories with package information Updating dependencies (including require-dev) Nothing to install or update Package guzzle/guzzle is abandoned, you should avoid using it. Use guzzlehttp/guzzle instead. Generating autoload files > php artisan optimize     [ReflectionException]                                              Class Saveeo\Board\Observers\BoardEventListener does not exist   

BoardEventListener.php (placed in Saveeo/Board/Observers)

<?php  namespace Saveeo\Board\Observers;  use Saveeo\Services\HashIds\Contracts\HashIds as HashIdService;  class BoardEventListener {     private $hashIdService;      public function __construct(HashIdService $hashIdService) {         $this->hashIdService = $hashIdService;     }      public function whenBoardIsCreated($event) {         $this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');     }      public function whenBoardIsUpdated($event) {         $this->hashIdService->syncHashIdValueOnModelChanges($event, 'board');     }      public function subscribe($events) {         $events->listen(             'Saveeo\Board\Observers\Events\BoardHasBeenCreated',             'Saveeo\Board\Observers\BoardEventListener@whenBoardIsCreated'         );          $events->listen(             'Saveeo\Board\Observers\Events\BoardHasBeenUpdated',             'Saveeo\Board\Observers\BoardEventListener@whenBoardIsUpdated'         );      } } 

EventServiceProvider.php (placed in Saveeo/Providers)

<?php  namespace Saveeo\Providers;  use Illuminate\Contracts\Events\Dispatcher as DispatcherContract; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;  class EventServiceProvider extends ServiceProvider {     /**      * The event listener mappings for the application.      *      * @var array      */     protected $listen = [         //     ];      /**      * The subscriber classes to register.      *      * @var array      */     protected $subscribe = [         'Saveeo\Board\Observers\BoardEventListener',     ];      /**      * Register any other events for your application.      *      * @param  \Illuminate\Contracts\Events\Dispatcher  $events      * @return void      */     public function boot(DispatcherContract $events) {         parent::boot($events);          //     } } 

Here is the folder structure. Can't see anything wrong here?

https://imgur.com/BI44Lq6

Composer.json

{     "name": "laravel/laravel",     "description": "The Laravel Framework.",     "keywords": [         "framework",         "laravel"     ],     "license": "MIT",     "type": "project",     "require": {         "php": ">=5.5.9",         "laravel/framework": "5.2.*",         "firebase/php-jwt": "~2.0",         "guzzlehttp/guzzle": "5.*",         "guzzlehttp/oauth-subscriber": "0.2.0",         "laravel/socialite": "2.*",         "league/flysystem-aws-s3-v3": "~1.0",         "aws/aws-sdk-php": "3.*",         "bugsnag/bugsnag-laravel": "1.*",         "vinkla/hashids": "^2.3"     },     "require-dev": {         "fzaninotto/faker": "~1.4",         "mockery/mockery": "0.9.*",         "phpunit/phpunit": "~4.0",         "phpspec/phpspec": "~2.1",         "tymon/jwt-auth": "0.5.*",         "symfony/dom-crawler": "~3.0",         "symfony/css-selector": "~3.0"     },     "autoload": {         "classmap": [             "database"         ],         "psr-4": {             "Saveeo\\": "app/"         }     },     "autoload-dev": {         "classmap": [             "tests/TestCase.php"         ]     },     "scripts": {         "post-install-cmd": [         "php artisan clear-compiled",         "php artisan optimize"     ],     "pre-update-cmd": [         "php artisan clear-compiled"     ],     "post-update-cmd": [         "php artisan optimize"     ],     "post-root-package-install": [         "php -r \"copy('.env.example', '.env');\""     ],     "post-create-project-cmd": [         "php artisan key:generate"     ]     },     "config": {         "preferred-install": "dist"     } } 

3 Answers

Answers 1

This seems to be related to the Composer PSR autoloading. By default Laravel will include

"autoload": {     "psr-4": {         "App\\": "app/"     } } 

which means "the root of the App namespace is in the app folder, and from there match namespace to folder structure".

Your Saveeo namespace hasn't been registered (it's not in the App hierarchy), so Composer doesn't know its location.

It should work if you add another line to your composer.json (and then dump-autoload)

"autoload": {     "psr-4": {         "App\\": "app/",         "Saveeo\\": "app/Saveeo"     } } 

Alternatively, you could, as the other answer points out, just place the whole Saveeo namespace within App (so it's App\Saveeo\Board\Observers\Events\BoardHasBeenCreated for instance). However your Saveeo namespace seems to be sort of a self-contained module, it may be more reasonable to have it as a separate namespace rather than renaming the namespace in all of its files.

Answers 2

It looks like we changed the autoloading namespace for the classes in the app/ directory from App (the default) to Saveeo in composer.json:

"autoload": {     "psr-4": {         "Saveeo\\": "app/"     } }, 

This is perfectly fine to do when we understand the implications. We can see the issue described in the question when we take a look at the project's folder structure for the file that causes the exception (with the namespaces in parentheses):

app                    (Saveeo) ├── Saveeo             (Saveeo\Saveeo) │   ├── Board          (Saveeo\Saveeo\Board) │   │   ├── Observers  (Saveeo\Saveeo\Board\Observers) │   │   │   ├── BoardEventListener └── ... 

For compatibility with PSR-4, the namespace for BoardEventListener should be Saveeo\Saveeo\Board\Observers because it exists in the Saveeo/ directory nested under app/. PSR-4 autoloading implementations resolve class files based on file names and paths, not the namespaces declared in the files. [Composer does read the namespace from the class files to create an optimized classmap, as long as the top-level namespace matches. See update.]

If we don't want to change the directory structure of the application, and we also don't want to use two Saveeos in the namespace, we can configure Composer to merge both directories into the same namespace when it autoloads classes:

"autoload": {     "psr-4": {         "Saveeo\\": [ "app/", "app/Saveeo/" ]     } }, 

...and remember to composer dump-autoload.

It works, but I don't recommend this in practice. It deviates from the PSR-4 standard, makes the application susceptible to namespace conflicts, and may be confusing to other people working on the project. I suggest that you flatten the Saveeo namespace and directory, or choose a different namespace for the classes in the nested directory.

...things work just fine if I run composer dump-autoload -o but I am curious why would this throw an error when I run composer update...?

When generating the autoloading cache file, Composer doesn't actually execute your code. However, the artisan optimize command, which runs after composer update in most Laravel applications, actually boots the application to perform its operations, so the problem code executes and we see the exception.

Update:

If using Saveeo\Board\etc was wrong versus doing Saveeo\Saveeo\Board\etc then other files around the entire application wouldn't work, but they do.

We can technically use non-PSR-4 namespaces that don't match the project's directory structure when we generate an optimized autoloader like we would to prepare an application for production:

composer dump-autoload --optimize  

This works because Composer will scan each class file for namespaces to create a static classmap. When we don't specify --optimize, Composer relies on dynamic autoloading which matches file paths to namespaces, so non-standard namespaces or directory structures fail to resolve.

The namespacing for the project in question works for the most part, even though it doesn't follow PSR-4, because we're manually dumping an optimized autoloader by using the -o option for dump-autoload. However, we see the error message because composer update removes the cached classmap before running the update, so, by the time the artisan optimize command runs, the classmap no longer contains the classes that we dumped.

We can configure Composer to always optimize the autoloader by setting the optimize-autoloader configuration directive in composer.json:

"config": {      "optimize-autoloader": true  } 

...which should fix the install and update commands for this project. It's a bit of a hack to get non-standard namespacing to resolve. With this approach, remember that we'll need to dump the autoloader whenever we add a new file in development that doesn't follow PSR-4.

Answers 3

I believe if you just change your namespace to:

App\Saveeo\Board\Observers instead of Saveeo\Board\Observers

all your problems should be solved. Please let me know if you have a reason for creating this new namespace, instead of just branching out from the base App namespace.

Read More

Wednesday, October 25, 2017

How to add jump-to functionality using laravel

Leave a Comment

I want to implement a jump to functionality. It is basically like a breadcrums but not exactly. It is a dropdown and can have left and right button. Please see my code below:

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">      <div class="btn-group" role="group" aria-label="...">    <a href="previousItemIfHas" class="btn btn-default">←</a>      <div class="btn-group" role="group">      <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">        Jump to        <span class="caret"></span>      </button>      <ul class="dropdown-menu">        <li><a href="#">Assignment1</a></li>        <li><a href="#">Quiz2</a></li>        <li><a href="#">Quiz4</a></li>        <li><a href="#">Assignment2</a></li>      </ul>    </div>        <a href="nextItemIfHas" class="btn btn-default">→</a>  </div>        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>

Controller

$course = Course::with([    'assignments' => $undeleted,    'quizzes' => $undeleted,    'add_links' => $undeleted ])->findOrFail($course_id); $course_items = collect($course->assignments); $course_items = $course_items->merge(collect($course->quizzes)); $course_items = $course_items->merge(collect($course->add_links)); $course_items = $course_items->sortBy('item_number'); 

Result want:
If the $course loops, it can list the items sort by item_number. If you are click the first item, then there should be no left arrow, same with the last item, if you click the last item, there should be no right arrow. The list of items are listed in the dropdown I've created.

Problem I don't have any idea how can I add a condition if the item is the first item so I can remove the left button, and same with the last item.

Note: I'm using laravel 5.1

2 Answers

Answers 1

i think you mean in the template:

you can use

@foreach ($users as $user)   <p>This is user {{ $user->id }}</p> @endforeach 

inside the "foreach" loop you has the variable loop

@foreach ($users as $user)  @if ($user == reset($users ))      This is the first iteration. @endif  @if ($user == end($users))     This is the last iteration. @endif  <p>This is user {{ $user->id }}</p>  @endforeach 

so you can see if first or last item

more:

http://php.net/manual/en/function.reset.php

http://php.net/manual/en/function.end.php

Answers 2

Hello Try this

it will start with no prev button, when you click on any thing has prev, prev will be shown, also when you click on anything has no nex the next will be hidden.. and so on..

give id="next" to next and id="prev" to prev buttons

Please run this snippet

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">      <div class="btn-group" role="group" aria-label="...">    <a href="previousItemIfHas" class="btn btn-default" id="prev">←</a>      <div class="btn-group" role="group">      <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">        Jump to        <span class="caret"></span>      </button>      <ul class="dropdown-menu">        <li><a href="#">Assignment1</a></li>        <li><a href="#">Quiz2</a></li>        <li><a href="#">Quiz4</a></li>        <li><a href="#">Assignment2</a></li>      </ul>    </div>        <a href="nextItemIfHas" class="btn btn-default" id="next">→</a>  </div>        <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>  <script>  $('#prev').hide();  $(".dropdown-menu li").click(function(){        if($(this).next('li').length <= 0) {        $('#next').hide();        } else {            $('#next').show();        }          if($(this).prev('li').length <= 0) {             $('#prev').hide();        } else {             $('#prev').show();        }           });  </script>

Read More

Friday, August 25, 2017

Laravel 5.0 multiauth

Leave a Comment

I have an application which has two parts back-end, and front-end. In the back-end admin can log in, and in the front-end the client can log in. Now it has been implemented. All application's query is done by logged in user id in both admin and client end.

Now my app needs a functionality where admin can view client data as same as client see their profile.There are a lot of things in client end. I can you use Auth::loginUsingId($client_id). Here client profile is showing perfectly but admin loggin session is lost as expected.

How to achieve this while admin login remain and admin can see client full data?

5 Answers

Answers 1

Let me introduce the simpliest way to have login as client functionality. First, define asuser and returnback routes.

Routes and actions

Route::get('/asuser/{user}', 'AdminController@asuser')         ->where('user', '[0-9]+')         ->name('asuser'); Route::get('/returnback', 'ClientController@returnback')         ->name('returnback'); 

In admin's controller:

public function asuser(User $client, Request $request) {     /* insert checking if user has right either here with some field       * like $user->is_admin or using middleware settings and Policy      */     # who user is     $fromId = Auth::user()->getId();      # logging as a client     Auth::login($client, true);      # but keeping admin in a session     $request->session()->put('adm_id', $fromId);      return redirect()->route('some-client-route')                     ->with('status', 'You are logged in as a client'); } 

And for returning back ClientController

public function returnback(Request $request) {     $fromId = Auth::user()->getId();      # getting admin id     $admId = $request->session()->pull('adm_id');     $adminUser = User::find($admId);      if (!$adminUser) {         return redirect()->back()                         ->with('status', 'Not allowed');     }      # logging out as a client and logging in as admin     Auth::logout();     Auth::login($adminUser, true);      return redirect()->route('some-admin-route')                     ->with('status', 'Welcome back!'); } 

Is it ready for production

No, it's not. That's not a great solution, it's just a glimpse how to use it. Sessions have lifetime, so if admin doesn't return back in its lifetime, session variables are lost and he becomes a client (if remember me=true, as in the code above). You can store value not in a session but in a database column.

In addition as t1gor mentioned, you must pay attention to the fact that you can't log client's actions and send events when admin is a client. That's the most serious problem of logging as a client. Anyway, I suppose, it is easier to solve that, than to move all the auth logic out of the views.

Well, hope it is helpful.

Answers 2

I think a good way to manage client/user profiles is to implement an user management section at your backend, display and edit your users and their profiles there.

Answers 3

I think middleware is the best possible option to filter the content between the admin and the normal user,because the code in the middleware run before any function call.

You just only need to set the usertype in the session and filter accordingly.

Visit:https://laravel.com/docs/5.4/middleware

Answers 4

Laravel does not provide mixed sessions. You can only be authenticated as one user at a time. If you really need this kind functionality in Laravel 5.0 you could solve this by hackish user ping-pong (e.g. login temporarily as client and switching back to admin right after).

But it seems like your problem is more Authorization-related (in contrast to Authentication). Laravel implemented an authorization layer in v5.1.11. Since v5.0 is not supported anymore you should update regardless of this feature.

You can find more information about authorization in the official documentation: https://laravel.com/docs/5.1/authorization

Answers 5

I would rather suggest you separate the view logic e.g. business logic into some common layer rather then doing a "login-as-client" functionality. Even though it looks like a short-cut, you'll have a whole lot of things to think about.

For instance, how do you log application events now? Add a check everwhere that the session has a adm_id and log it instead of userId? This is just one example.

What I would have done:

  1. Separate the view (e.g. user profiles, user content, etc.) from the session so that it is accessed by the ID in the URL or whatever else method, not by currently logged in user id.

  2. Implement a propper role-based ACL. There are plenty of packages already. In your example, you wouold have an admin role and a client role, both havin permission object view-client-profile, for instance.

In the end, this might take a lot more time for development, but would defenitely save you some time debugging/troubleshooting with the angry client on the phone. Hope that helps.

Read More

Wednesday, August 23, 2017

Call Artisan Commands via Code NOT Working

Leave a Comment

Description

In my Laravel application, I run

php artisan languages:export 

I got a csv file to export successfully base on my languages table.

BUT the goal is to leverage this Artisan::call from Laravel, but when I did this in my code

$export = Artisan::call('languages:export'); 

Result

I kept getting 0 as my result of $export variable - no file exported of course.


Update

Now I try to call it via shell_exec() and exec()

$cmd = 'php '.base_path().'/artisan languages:export'; $export = shell_exec($cmd); 

I see nothing generated on either one.

From the command line interface, I run

php /Applications/MAMP/htdocs/code/artisan languages:export

I saw my csv file generated.


How would one go about and call artisan commands via code ?

3 Answers

Answers 1

Your console command isn't going to return the CSV file that way, because that's not how console commands work. The fact that you're getting 0 is actually a good thing console land (it means it completed without throwing errors).

Your call is correct, it's your expectation that is wrong. You'll need to change your approach for however you're consuming $export. If you want to actually access the newly-created CSV, have a look at the phpleague/csv package and/or the built in fopen command. If you just want to know that it completed successfully, then just take that 0 as a success.

Answers 2

Try to check output by calling dd(Artisan::output()); after $export = Artisan::call('languages:export');

Answers 3

You could also try the exec() function with php artisan languages:export as parameter. You may need to include the right path in front of the command in that case.

I've also found the function Artisan::command instead of call, maybe that works?

Read More

Monday, August 14, 2017

Laravel 5.4 using Mail fake with Mail::queue, Mail::assertSent not working

Leave a Comment

I am writing unit test for a code that sends email with Mail::queue function, like the one in the documentation: https://laravel.com/docs/5.4/mocking#mail-fake

My Test:

/** @test */ public function send_reminder() {      Mail::fake();       $response = $this->actingAs($this->existing_account)->json('POST', '/timeline-send-reminder', []);      $response->assertStatus(302);       Mail::assertSent(ClientEmail::class, function ($mail) use ($approver) {           return $mail->approver->id === $approver->id;      }); } 

Code being Tested:

Mail::to($email, $name)->queue(new ClientEmail(Auth::user())); 

Error Message:

The expected [App\Mail\ClientEmail] mailable was not sent. Failed asserting that false is true. 

The email is sent when I manually test it, but not from Unit Test. I'm thinking it might be because I am using Mail::queue instead of Mail::send function.

In .env file, I have

QUEUE_DRIVER=sync and MAIL_DRIVER=log 

How can I test Mail::queue for Laravel?

1 Answers

Answers 1

I have searched around this issue I found this

https://stackoverflow.com/a/44354201/5853931

I hope it helps you

Read More