jesse_orange_newable

jesse_orange_newable

Member Since 1 Year Ago

Experience Points
7,250
Total
Experience

2,750 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed
51
Lessons
Completed
Best Reply Awards
0
Best Reply
Awards
  • start your engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-in-session Created with Sketch.

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • subscriber Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • evangelist Created with Sketch.

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

Level 2
7,250 XP
May
13
3 weeks ago
Activity icon

Replied to Unknown Error When Logging Out

To all who are interested, it turns out the Listener for my Logout event was using the wrong Request class,.

I had imported use Illuminate\Http\Client\Request;

When it fact it should have been use Illuminate\Http\Request;.

I solved this because you guys mentioned checking my facades, so I had a comb through literally everything and found this rather annoying issue.

May
12
3 weeks ago
Activity icon

Started a new Conversation Unknown Error When Logging Out

In my application I decided to make my own LogoutController as I didn't see the necessity of using Auth::routes


<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Auth;

class LogoutController extends Controller
{
    /**
     * Logout and redirect to the login page
     *
     * @return void
     */
    public function logout(Request $request)
    {
        Auth::logout();

        $request->session()->invalidate();

        $request->session()->regenerateToken();

        return redirect()->route('login.ms');
    }
}

However, every time i try to actually log out i get the following error:

Illuminate\Contracts\Container\BindingResolutionException: Unresolvable dependency resolving [Parameter #0 [ $request ]] in class Illuminate\Http\Client\Request

I've never seen this error in my life and I'm certain it refers to something being added to the container but am unsure why this affects logging out specifically.

Activity icon

Replied to Testing Socialite Drivers

That's fair, I just think where Socialite does a lot of the heavy lifting initially there seems to be no reason to deep dive, I think I'm gonna go read up on Mocking a little more as you must be able to mock the provider.

Activity icon

Replied to Testing Socialite Drivers

I'm just surprised that the documentation is a little thin on this, because obviously you would expect it to work but also because i am then potentially creating a user from the returned Socialite user object, I kinda need to check.

I think the rest of the test classes are perhaps overly kind because you can just fake everything whereas i haven't seen Socialite::fake() anywhere.

If that were the case you could just provide a dummy user object, but in my login controller I have this:


try {
    $user_oath = Socialite::with('graph')->stateless()->user();
} catch (RequestException  $e) {
    Log::warning('A user attempted to login using an expired authorisation token using IP: ' . \Request::ip());
    return redirect()->route('login.ms');
} catch (\Exception $e) {
    Log::warning('A user could not login and was redirected back to the login screen using IP: ' . \Request::ip());
    return redirect()->route('login.ms');
}

So if you don't provide a valid set of parameters you get redirected anyway, so in my test I just get redirected.

Activity icon

Replied to Testing Socialite Drivers

So essentially just pretend i have a response from Socialite? That makes sense, I'll try it and post any updates.

May
11
3 weeks ago
Activity icon

Started a new Conversation Testing Socialite Drivers

In my application I have a LoginController that uses a third party Socialite driver in order to login using Microsoft OAuth.

https://socialiteproviders.netlify.app/providers/microsoft-graph.html

Testing the redirect was dead simple:


<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class LoginTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function it_is_redirected_to_microsoft_graph_login()
    {
        $response = $this->get(route('login.ms'));

        $this->assertStringContainsString('login.microsoftonline.com/common/oauth2/v2.0/authorize', $response->getTargetUrl());
    }

    /** @test */
    public function it_receives_graph_login_and_returns_user()
    {
        $response = $this->get(route('login.ms.callback'));

        // TODO: How do you test Socialite provider callback?
    }
}

I have no idea how to test the call back however.

Here is my LoginController


<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Socialite;
use App\User;
use Log;
use Auth;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('guest')->except('logout');

        $this->citibase_email_domains = [
            '@citibase.co.uk', '@easyhub.com',
        ];

        $this->newable_email_domains = [
            '@newable.co.uk', '@tradelondon.org.uk', '@tradesoutheast.com',
        ];
    }

    /**
     * Redirect the user to the GitHub authentication page.
     *
     * @return \Illuminate\Http\Response
     */
    public function redirectToProvider()
    {
        return Socialite::driver('graph')->redirect();
    }

    /**
     * Obtain the user information from GitHub.
     *
     * @return \Illuminate\Http\Response
     */

    /**
     * Obtain the user information from OAuth and then either create or authenticate a user
     *
     * @return \Illuminate\Http\Response
     */
    public function handleProviderCallback(Request $request)
    {
        // Attempt to retreive a user through Microsoft Graph oAuth, if token has expired retry
        try {
            $user_oath = Socialite::with('graph')->stateless()->user();
        } catch (RequestException  $e) {
            Log::warning('A user attempted to login using an expired authorisation token using IP: ' . \Request::ip());
            return redirect()->route('login.ms');
        } catch (\Exception $e) {
            Log::warning('A user could not login and was redirected back to the login screen using IP: ' . \Request::ip());
            return redirect()->route('login.ms');
        }

        // Get the email used to authenticate
        $email = strtolower($user_oath->userPrincipalName);

        // Check to see if this user exists
        $user = User::withTrashed()->where('email', $email)->first();

        // Login the user if they exist
        if ($user) {
            // Restore this user if trashed
            if ($user->trashed()) {
                $user->restore();
            }

            // Login the user, setting a remember token
            Auth::login($user, true);

            // Redirect the user
            return redirect()->intended('/');
        } else {
            // If this user has a Citibase email address
            if (strpos_arr($email, $this->citibase_email_domains)) {
                //Create new user
                User::create([
                    'ms_id' => $user_oath->id,
                    'first_name' => $user_oath->givenName,
                    'last_name' => $user_oath->surname,
                    'email' => $user_oath->userPrincipalName,
                ]);
            } elseif (strpos_arr($email, $this->newable_email_domains)) {
                dd('Newable account');
                // NewableProfile::createNewableUser($email, $user_oath);
                return redirect()->intended('/');
            } else {
                // If no user was found but the user did not enter a valid email address
                Log::warning("{$email} attempted to access the Citibase Intranet but is not a recognised user");

                abort(403, 'Please ensure you are using a Citibase or Newable email address when attempting to access MyCitibase.');
            }
        }
    }
}


Essentially I'd like to test the conditions in the controller but how can I return a user from Socialite when I'd never enter my real email and password in a test?

Apr
20
1 month ago
Activity icon

Started a new Conversation Using Auth()->user() Within A Test

In an application I'm building a Comment can have a relation to both a User and an Article, so when creating the comment I just do the following:


$article->comments()->create($request->validated());

Then to associate the User I hook into the creating event of the Comment model to associate the user with the comment like so:


$comment->user()->associate(auth()->user());

However, in my test case it always squawks about comment user_id being null, which leads me to believe that in the test case auth()->user() is null.

How can this be so when I'm using actingAs() when calling the route to add the comment?

Apr
09
1 month ago
Activity icon

Started a new Conversation Optimising A Job Class

In my application I have a Job class that polls the DocuSign API for changes to envelopes, then if the envelope is seen as completed I update a bunch of fields and then place the signed file into local storage.

The class looks like this:


z<?php

namespace App\Jobs;

use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\CompanyRegistration;
use DocuSign;
use App\Notifications\RegistrationFormSigned;
use Carbon\Carbon;

class PollDocuSignEnvelopes implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 2;

    /**
     * Create a new job instance.
     */
    public function __construct()
    {
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Create a new DocuSign client
        $client = DocuSign::create();

        // Retrieve the application forms that have been created as envelopes, but not signed
        $envelopeIds = CompanyRegistration::select('envelope_id')
                            ->whereNotNull('envelope_id')
                            ->whereNull('envelope_signed_at')
                            ->pluck('envelope_id')
                            ->toArray();

        if ($envelopeIds) {
            // Define the options to be used when checking for changes
            $options = $client->envelopes->listStatusChangesOptions([
                'count' => 10,
                'envelope_ids' => $envelopeIds,
            ]);

            // Retrieve the changes via the listStatusChanges() API method
            $changes = $client->envelopes->listStatusChanges($options);

            // Iterate over the changed envelopes
            foreach ($changes['envelopes'] as $envelope) {
                $registration = CompanyRegistration::where('envelope_id', $envelope['envelope_id'])->first();

                $registration->update([
                    'envelope_status' => $envelope['status'],
                ]);

                // If the envelope is complete, timestamp it, notify the creator and grab the signed document
                if ($registration->envelope_status == 'completed') {
                    $path = "/registrations/{$registration->id}/signed.pdf";

                    // Retrieve the first document associated with the completed envelope
                    $document = $client->envelopes->getDocument(1, $registration->envelope_id);

                    $documentContents = file_get_contents($document->getPathname());

                    Storage::disk('local')->put($path, $documentContents);

                    $registration->update([
                        'envelope_signed_at' => Carbon::now(),
                        'envelope_completed_document_path' => $path,
                    ]);

                    $registration->user->notify(new RegistrationFormSigned($registration));
                }
            }
        }
    }

    /**
     * The job failed to process.
     *
     * @param  Exception $exception
     * @return void
     */
    public function failed(Exception $exception)
    {
        // Send user notification of failure, etc...
    }
}

I can't help think this is sub-optimal however, because I make a number of database calls and I can see a bunch of places it might fall over. Is it okay for jobs just to die silently, or should I break this into some listeners and trigger a main event from within the job?

E.g

Event

EnvelopeCompleted

Listeners

UpdateDocuSignStatus DownloadSignedFile

I'm in two minds...

Apr
07
1 month ago
Activity icon

Replied to Undefined Properties In Job

I have a basic question, that I've never really acknowledged as I've just gone with various examples from the documentation, it has two parts.

  1. If I could just create a new instance of what I need within the handle method, would I even need to pass it through the constructor, is it possible to just new up an object in the actual constructor?

  2. If I can just use $this within classes to access properties, is there any need to define properties, more so within job classes?

Lastly, within a job the handle method seems to be doing most of the work but I'm assuming you could pass attributes passed through the constructor?

Activity icon

Started a new Conversation Using Dependencies Inside Job Classes

When using a job in Laravel 6, how do you go about injecting dependencies? I read this is the documentation -

"The handle method is called when the job is processed by the queue. Note that we are able to type-hint dependencies on the handle method of the job. The Laravel service container automatically injects these dependencies."

Here is the example given:


<?php

namespace App\Jobs;

use App\Podcast;
use App\AudioProcessor;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessPodcast implements ShouldQueue
{
    use InteractsWithQueue, Queueable, SerializesModels;

    protected $podcast;

    /**
     * Create a new job instance.
     *
     * @param  Podcast  $podcast
     * @return void
     */
    public function __construct(Podcast $podcast)
    {
        $this->podcast = $podcast;
    }

    /**
     * Execute the job.
     *
     * @param  AudioProcessor  $processor
     * @return void
     */
    public function handle(AudioProcessor $processor)
    {
        // Process uploaded podcast...
    }
}

But say I wanted to use a 3rd party package, would it work in the same way?

Activity icon

Started a new Conversation Undefined Properties In Job

I am using DocuSign within my application and I created a job to accomplish polling, that is, checking whether a document was updated without using a webhook.

The job is called PollDocuSignEnvelopes and makes a call to the DocuSign API to check for changes.

Here is the code:


<?php

namespace App\Jobs;

use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\CompanyRegistration;
use App\DocuSign\DocuSign as DocuSignWrapper;
use App\Notifications\RegistrationFormSigned;

class PollDocuSignEnvelopes implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * The number of times the job may be attempted.
     *
     * @var int
     */
    public $tries = 2;

    /**
     * The instance of DocuSignWrapper
     *
     * @var DocuSign
     */
    public $docusignWrapper;

    /**
     * Create a new job instance
     *
     * @param \App\DocuSign\DocuSign $docusignWrapper
     */
    public function __construct()
    {
        $this->docuSignWrapper = new DocuSignWrapper;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Retrieve the application forms that have been sent
        $envelopeIds = CompanyRegistration::select('envelope_id')
            ->whereNotNull('envelope_id')
            ->pluck('envelope_id')
            ->toArray();

        $envelopes = $this->docuSignWrapper->pollEnvelopeChanges($envelopeIds);

        foreach ($envelopes as $envelope) {
            $registration = CompanyRegistration::where('envelope_id', $envelope['envelope_id'])
                ->first()
                ->update([
                    'envelope_status' => $envelope['status'],
                ]);

            // If the status comes back as completed, notify the creator
            if ($registration->is_completed) {
                $registration->user->notify(new RegistrationFormSigned($registration));
            }
        }
    }

    /**
     * The job failed to process.
     *
     * @param  Exception $exception
     * @return void
     */
    public function failed(Exception $exception)
    {
        // Send user notification of failure, etc...
    }
}

However, apparently DocuSignWrapper is undefined even though I'm clearly setting it? Am I not supposed to new up classes in the __construct() method?


ErrorException: Undefined property: App\Jobs\PollDocuSignEnvelopes::$docuSignWrapper in /homepages/6/d142103519/htdocs/development-sites/ita-portal/app/Jobs/PollDocuSignEnvelopes.php:56
Stack trace:
#0 /homepages/6/d142103519/htdocs/development-sites/ita-portal/app/Jobs/PollDocuSignEnvelopes.php(56): Illuminate\Foundation\Bootstrap\HandleExceptions->handleError(8, 'Undefined prope...', '/homepages/6/d1...', 56, Array)
#1 [internal function]: App\Jobs\PollDocuSignEnvelopes->handle()
#2 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): call_user_func_array(Array, Array)
#3 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#4 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#5 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#6 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#7 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(94): Illuminate\Container\Container->call(Array)
#8 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(130): Illuminate\Bus\Dispatcher->Illuminate\Bus\{closure}(Object(App\Jobs\PollDocuSignEnvelopes))
#9 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(App\Jobs\PollDocuSignEnvelopes))
#10 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Bus/Dispatcher.php(98): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#11 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(83): Illuminate\Bus\Dispatcher->dispatchNow(Object(App\Jobs\PollDocuSignEnvelopes), false)
#12 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(130): Illuminate\Queue\CallQueuedHandler->Illuminate\Queue\{closure}(Object(App\Jobs\PollDocuSignEnvelopes))
#13 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(105): Illuminate\Pipeline\Pipeline->Illuminate\Pipeline\{closure}(Object(App\Jobs\PollDocuSignEnvelopes))
#14 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(85): Illuminate\Pipeline\Pipeline->then(Object(Closure))
#15 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/CallQueuedHandler.php(59): Illuminate\Queue\CallQueuedHandler->dispatchThroughMiddleware(Object(Illuminate\Queue\Jobs\DatabaseJob), Object(App\Jobs\PollDocuSignEnvelopes))
#16 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Jobs/Job.php(88): Illuminate\Queue\CallQueuedHandler->call(Object(Illuminate\Queue\Jobs\DatabaseJob), Array)
#17 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(368): Illuminate\Queue\Jobs\Job->fire()
#18 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(314): Illuminate\Queue\Worker->process('database', Object(Illuminate\Queue\Jobs\DatabaseJob), Object(Illuminate\Queue\WorkerOptions))
#19 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Worker.php(134): Illuminate\Queue\Worker->runJob(Object(Illuminate\Queue\Jobs\DatabaseJob), 'database', Object(Illuminate\Queue\WorkerOptions))
#20 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(112): Illuminate\Queue\Worker->daemon('database', 'default', Object(Illuminate\Queue\WorkerOptions))
#21 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Queue/Console/WorkCommand.php(96): Illuminate\Queue\Console\WorkCommand->runWorker('database', 'default')
#22 [internal function]: Illuminate\Queue\Console\WorkCommand->handle()
#23 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): call_user_func_array(Array, Array)
#24 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\Container\BoundMethod::Illuminate\Container\{closure}()
#25 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\Container\Util::unwrapIfClosure(Object(Closure))
#26 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\Container\BoundMethod::callBoundMethod(Object(Illuminate\Foundation\Application), Array, Object(Closure))
#27 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\Container\BoundMethod::call(Object(Illuminate\Foundation\Application), Array, Array, NULL)
#28 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Console/Command.php(134): Illuminate\Container\Container->call(Array)
#29 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/symfony/console/Command/Command.php(255): Illuminate\Console\Command->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#30 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Console/Command.php(121): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Illuminate\Console\OutputStyle))
#31 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/symfony/console/Application.php(1001): Illuminate\Console\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#32 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/symfony/console/Application.php(271): Symfony\Component\Console\Application->doRunCommand(Object(Illuminate\Queue\Console\WorkCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#33 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/symfony/console/Application.php(147): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#34 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Console/Application.php(93): Symfony\Component\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#35 /homepages/6/d142103519/htdocs/development-sites/ita-portal/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(131): Illuminate\Console\Application->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#36 /homepages/6/d142103519/htdocs/development-sites/ita-portal/artisan(37): Illuminate\Foundation\Console\Kernel->handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#37 {main}

Apr
03
2 months ago
Activity icon

Replied to Accessing Relationship Within A Pivot Table Using BelongsToMany

So you have 4 models:

  • Centre
  • HealthAndSafetyDocument
  • CentreHealthAndSafetyDocument
  • Document

In what you've you've defined that a Centre can have many HealthAndSafetyDocument

Do you have a relation between Document and HealthAndSafetyDocument?

I'm wondering if it'd be easier to define an extra relation on the Centre which is a HasManyThrough so you could get to Document through HealthAndSafetyDocument?

A hasManyThrough is defined in this way:

    public function posts()
    {
        return $this->hasManyThrough(
            'App\Post',
            'App\User',
            'country_id', // Foreign key on users table...
            'user_id', // Foreign key on posts table...
            'id', // Local key on countries table...
            'id' // Local key on users table...
        );
    }

So you could have within Centre a relation like so:


    public function documents()
    {
        return $this->hasManyThrough(
            'App\Document', // distant table
            'App\HealthAndSafetyDocument', // middle table
            'centre_id', // Foreign key on health safety table...
            'health_document_id', // Foreign key on documents table...
            'id', // Local key on this table
            'id' // Local key on middle table
        );
    }

Apr
02
2 months ago
Activity icon

Started a new Conversation Avoiding Duplication When Using Similar Models

In my application I have two models: Registration and Outcome these are used to hold data from two different forms.

Here is the schema for Registrations whose table is called company_registrations


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateCompanyRegistrationsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('company_registrations', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->unsignedBigInteger('user_id');
            $table->string('envelope_id')->nullable();
            $table->string('envelope_status')->nullable();
            $table->foreign('user_id')->references('id')->on('users');
            // Company details
            $table->string('contact_name')->nullable();
            $table->string('international_trade_advisor')->nullable();
            $table->string('position')->nullable();
            $table->string('company_name')->nullable();
            $table->string('telephone_number')->nullable();
            $table->string('company_registration_number')->nullable();
            $table->string('unique_tax_reference')->nullable();
            $table->text('address')->nullable();
            $table->string('email')->nullable();
            $table->string('website')->nullable();
            // SME fields
            $table->boolean('employs_less_than_250_people')->nullable();
            $table->boolean('annual_turnover_below_50_million')->nullable();
            $table->boolean('balance_sheet_below_50_million')->nullable();
            $table->boolean('is_subsidary')->nullable();
            // Difficulty
            $table->boolean('is_undertaking_in_difficulty')->nullable();
            $table->boolean('insolvency_proceedings')->nullable();
            $table->boolean('share_accumulated_losses')->nullable();
            $table->boolean('in_receipt_of_rescue_aid')->nullable();
            // Other details
            $table->timestamp('date_of_incorporation')->nullable();
            $table->integer('employee_count')->nullable();
            $table->string('annual_turnover')->nullable();
            $table->string('sector')->nullable();
            // About company
            $table->text('products_services')->nullable();
            $table->text('countries_of_interest')->nullable();
            $table->boolean('is_intending_to_grow_export_business')->nullable();
            $table->boolean('is_making_new_market_sales')->nullable();
            $table->boolean('has_exported_in_last_12_months')->nullable();
            // Financial information
            $table->string('financial_year_end')->nullable();
            $table->string('turnover')->nullable();
            $table->string('export_turnover')->nullable();
            $table->string('total_salary_wage_cost')->nullable();
            $table->string('deprecation')->nullable();
            $table->string('net_profit_before_tax')->nullable();
            // Diversity
            $table->string('ethnic_ownership_majority')->nullable();
            $table->string('sex_ownership_majority')->nullable();
            $table->string('disability_ownership_majority')->nullable();
            $table->string('signer_age_group')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('company_registrations');
    }
}


The other table is kind of similar but has a few different fields, so I separated it into a new model.

To add a little more context, I'm using these forms to populate a DocuSign template.

The code for which is duplicated somewhat in both the RegistrationController and OutcomeController

The RegistrationController looks like this:


<?php

namespace App\Http\Controllers;

use App\CompanyRegistration;
use App\Http\Requests\StoreRegistration;
use App\Http\Requests\UpdateRegistration;
use App\Jobs\PollDocuSignEnvelopes;
use Illuminate\Http\Request;
use DocuSign;

class RegistrationController extends Controller
{
    /**
     * Create a new controller instance
     */
    public function __construct()
    {
        $this->client = DocuSign::create();
    }

    /**
     * Display a listing of all my registration forms
     */
    public function index()
    {
        $registrations = auth()->user()->registrations;

        PollDocuSignEnvelopes::dispatchNow();

        return view('forms.registrations.index', compact('registrations'));
    }

    /**
     * Display the view that holds the application form.
     */
    public function create()
    {
        $tempplateDefinition = $this->client->templates->get(CompanyRegistration::DOCUSIGN_TEMPLATE_ID);

        $tabs = $tempplateDefinition['recipients']['signers'][0]['tabs'];

        dump($tabs);

        return view('forms.registrations.create');
    }

    /**
     * Store an application in the database
     *
     * @param \Illuminate\Http\Request $request
     */
    public function store(StoreRegistration $request)
    {
        $attributes = $request->validated();

        $registration = auth()->user()->registrations()->create([
            'contact_name' => $attributes['contact_name'],
            'international_trade_advisor' => $attributes['international_trade_advisor'],
            'position' => $attributes['position'],
            'company_name' => $attributes['company_name'],
            'telephone_number' => $attributes['telephone_number'],
            'company_registration_number' => $attributes['company_registration_number'],
            'address' => $attributes['address'],
            'email_address' => $attributes['email'],
            'website' => $attributes['website'],
            'employs_less_than_250_people' => $attributes['employs_less_than_250_people'] ?? 0,
            'annual_turnover_below_50_million' => $attributes['annual_turnover_below_50_million'] ?? 0,
            'balance_sheet_below_50_million' => $attributes['balance_sheet_below_50_million'] ?? 0,
            'is_subsidary' => $attributes['is_subsidary'] ?? 0,
            'is_undertaking_in_difficulty' => $attributes['is_undertaking_in_difficulty'] ?? 0,
            'date_of_incorporation' => $attributes['date_of_incorporation'],
            'employee_count' => $attributes['employee_count'],
            'annual_turnover' => $attributes['annual_turnover'],
            'sector' => $attributes['sector'],
            'products_services' => $attributes['products_services'],
            'countries_of_interest' => $attributes['countries_of_interest'],
            'is_intending_to_grow_export_business' => $attributes['is_intending_to_grow_export_business'] ?? 0,
            'is_making_new_market_sales' => $attributes['is_making_new_market_sales'] ?? 0,
            'has_exported_in_last_12_months' => $attributes['has_exported_in_last_12_months'] ?? 0,
            'financial_year_end' => $attributes['financial_year_end'],
            'turnover' => $attributes['turnover'],
            'export_turnover' => $attributes['export_turnover'],
            'total_salary_wage_cost' => $attributes['total_salary_wage_cost'],
            'deprecation' => $attributes['deprecation'],
            'net_profit_before_tax' => $attributes['net_profit_before_tax'],
        ]);

        $this->addSupporters($registration, $attributes);

        return redirect()->route('registrations.edit', $registration)->withSuccess('Registration form saved. Now please review the details.');
    }

    /**
     * Display this registration
     *
     * @param \App\CompanyRegistration $registration
     */
    public function show(CompanyRegistration $registration)
    {
        return view('forms.registrations.show', compact('registration'));
    }

    /**
     * Display the form to edit a company registration
     *
     * @param \App\CompanyRegistration $registration
     */
    public function edit(CompanyRegistration $registration)
    {
        // $this->authorize('update', $registration);

        return view('forms.registrations.edit', compact('registration'));
    }

    /**
     * Store an application in the database
     *
     * @param \Illuminate\Http\Request $request
     */
    public function update(UpdateRegistration $request, CompanyRegistration $registration)
    {
        // $this->authorize('update', $registration);

        $attributes = $request->validated();

        $registration->update([
            'contact_name' => $attributes['contact_name'],
            'international_trade_advisor' => $attributes['international_trade_advisor'],
            'position' => $attributes['position'],
            'company_name' => $attributes['company_name'],
            'telephone_number' => $attributes['telephone_number'],
            'company_registration_number' => $attributes['company_registration_number'],
            'address' => $attributes['address'],
            'email_address' => $attributes['email'],
            'website' => $attributes['website'],
            'employs_less_than_250_people' => $attributes['employs_less_than_250_people'] ?? 0,
            'annual_turnover_below_50_million' => $attributes['annual_turnover_below_50_million'] ?? 0,
            'balance_sheet_below_50_million' => $attributes['balance_sheet_below_50_million'] ?? 0,
            'is_subsidary' => $attributes['is_subsidary'] ?? 0,
            'is_undertaking_in_difficulty' => $attributes['is_undertaking_in_difficulty'] ?? 0,
            'date_of_incorporation' => $attributes['date_of_incorporation'],
            'employee_count' => $attributes['employee_count'],
            'annual_turnover' => $attributes['annual_turnover'],
            'sector' => $attributes['sector'],
            'products_services' => $attributes['products_services'],
            'countries_of_interest' => $attributes['countries_of_interest'],
            'is_intending_to_grow_export_business' => $attributes['is_intending_to_grow_export_business'] ?? 0,
            'is_making_new_market_sales' => $attributes['is_making_new_market_sales'] ?? 0,
            'has_exported_in_last_12_months' => $attributes['has_exported_in_last_12_months'] ?? 0,
            'financial_year_end' => $attributes['financial_year_end'],
            'turnover' => $attributes['turnover'],
            'export_turnover' => $attributes['export_turnover'],
            'total_salary_wage_cost' => $attributes['total_salary_wage_cost'],
            'deprecation' => $attributes['deprecation'],
            'net_profit_before_tax' => $attributes['net_profit_before_tax'],
        ]);

        $this->addSupporters($registration, $attributes);

        return redirect()->route('registrations.show', $registration)->withSuccess('Details confirmed. Pleae review and send');
    }

    /**
     * Add registration supports to this registration
     *
     * @param \App\CompanyRegistration $registration
     * @param array                    $attributes
     */
    private function addSupporters(CompanyRegistration $registration, array $attributes)
    {
        $supporters = $attributes['registration_supporters'];

        if (is_array($supporters) && count($supporters) > 0) {
            foreach ($supporters as $support) {
                $registration->supporters()->create([
                    'funding_body' => $support['funding_body'],
                    'purpose' => $support['purpose'],
                    'amount' => $support['amount'],
                ]);
            }
        }
    }

    /**
     * Send the data in this form to a signer via DocuSign
     *
     * @param \App\CompanyRegistration $registration
     */
    public function sendForSigning(CompanyRegistration $registration, Request $request)
    {
        $data = $request->validate([
            'signer_name' => 'required|string|max:191',
            'signer_email' => 'required|string|email|max:191',
        ]);

        // Populate the template data attributes
        $defaultSigner = $this->client->templateRole([
            'email' => $data['signer_email'],
            'name' => $data['signer_name'],
            'role_name' => 'Signer 1',
            'routing_order' => 1,
            'tabs' => [
                'textTabs' => [
                    [
                        'tabLabel' => 'Contact name',
                        'value' => $registration->contact_name,
                    ],
                    [
                        'tabLabel' => 'International Trade Advisor',
                        'value' => $registration->international_trade_advisor,
                    ],
                    [
                        'tabLabel' => 'Position',
                        'value' => $registration->position,
                    ],
                    [
                        'tabLabel' => 'Company Name',
                        'value' => $registration->company_name,
                    ],
                    [
                        'tabLabel' => 'Telephone number',
                        'value' => $registration->telephone_number,
                    ],
                    [
                        'tabLabel' => 'Company registration number (CRN)',
                        'value' => $registration->company_registration_number,
                    ],
                    [
                        'tabLabel' => 'Address',
                        'value' => $registration->address,
                    ],
                    [
                        'tabLabel' => 'Email',
                        'value' => $registration->email,
                    ],
                    [
                        'tabLabel' => 'Website',
                        'value' => $registration->website,
                    ],
                ],
            ],
        ]);

        // Define the DocuSign envelope
        $envelopeDefinition = $this->client->envelopeDefinition([
            'status' => 'sent',
            'email_subject' => 'SME Application Form - Please review and sign',
            'template_id' => CompanyRegistration::DOCUSIGN_TEMPLATE_ID,
            'template_roles' => [
                $defaultSigner,
            ],
        ]);

        // Retrieve the envelope summary
        $envelopeSummary = $this->client->envelopes->createEnvelope($envelopeDefinition);

        // Update the envelope details on the model
        $registration->update([
            'envelope_id' => $envelopeSummary['envelope_id'],
            'envelope_status' => $envelopeSummary['status'],
        ]);

        return redirect()->route('dashboard')
            ->withSuccess('Registration form for ' . $registration->company_name . ' sent to ' . $data['signer_email']);
    }
}

The sendForSigning() method is going to be literally identical, minus the template ID and the data passed in.

IMHO this feels a little dirty because huge chunks will be replicated.

The same applies for the notifications of RegistrationSent and OutcomeSent, the only difference being the model type that gets passed in.

I also have a job that polls DocuSign for updates that feels repetitive.


<?php

namespace App\Jobs;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\CompanyRegistration;
use App\DocuSign\DocuSign as DocuSignWrapper;
use App\Notifications\RegistrationFormSigned;

class PollDocuSignEnvelopes implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance
     *
     * @param \App\DocuSign\DocuSign $docusignWrapper
     */
    public function __construct()
    {
        $this->docusignWrapper = new DocuSignWrapper;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        // Retrieve the application forms that have been sent
        $registationEnvelopes = CompanyRegistration::select('envelope_id')
            ->isSentDocuSign()
            ->pluck('envelope_id')
            ->toArray();

        // Merge all the envelope IDs into one big array
        $envelopeIds = array_merge($registationEnvelopes);

        $envelopes = $this->docusignWrapper->pollEnvelopeChanges($envelopeIds);

        // Loop through the given envelopes and update the statuses
        foreach ($envelopes as $envelope) {
            $registration = CompanyRegistration::where('envelope_id', $envelope['envelope_id'])->first();

            $registration->update([
                'envelope_status' => $envelope['status'],
            ]);

            // If the status comes back as completed, notify the creator
            if ($registration->is_completed) {
                $registration->user->notify(new RegistrationFormSigned($registration));
            }
        }
    }
}

You can see how that would also become repetitive.

So, my question is: is this a bad thing, because the data in each form is technically a different entity?

Mar
05
2 months ago
Activity icon

Replied to Login - Infinite Loop

My solution in the end was to use sub-queries in a few different places, but using the same principle.

Activity icon

Replied to Login - Infinite Loop

This actually makes a lot of sense, however I decided to use a global scope because not active users should not appear in searches, on team pages etc.

Given what you've said, should I just use a local scope?

Activity icon

Started a new Conversation Login - Infinite Loop

I am using Microsoft OAuth to handle logins within my application.

Here is a snippet:


/**
 * Handle what we do after we retrieve an email from the Oauth provider
 *
 * @param array $user_oauth
 */
private function handleOauthAuthenticationAttempt($user_oauth)
{
    //Retrieve email from Oauth
    $email = strtolower($user_oauth->userPrincipalName);

    //Check to see if the logged in user's email exists
    $user = User::withoutGlobalScope('active')
        ->withTrashed()
        ->where('email', $email)
        ->first();

    if ($user) {
        Auth::login($user);

        return redirect()->intended('/welcome');
    } elseif (!$user && strpos($email, '@citibase.co.uk')) {
        $user = User::withTrashed()->updateOrCreate(
            [
                'username' => strtolower($user_oauth->givenName . substr($user_oauth->surname, 0, 1)),
            ],
            [
                'email' => $user_oauth->userPrincipalName,
                'display_name' => $user_oauth->givenName . ' ' . $user_oauth->surname,
                'role' => 'Citibase employee',
                'department' => 'Citibase',
                'location' => 'Citibase Centres',
                'direct_dial_in' => null,
                'mobile_number' => null,
                'managed_by' => null,
            ]
        );

        Auth::login($user);

        return redirect()->intended('/welcome');
    } else {
        Log::warning("{$email} attempted to access the Newable Intranet but is not a recognised user");

        abort(403, 'Please ensure you are using a Citibase or Newable email address when attempting to access MyNewable.');
    }
}

Since adding my global scope it causes an infinite loop.

The scope is defined in the User model


    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('active', function (Builder $builder) {
            $builder->where('is_active', 1);
        });
    }

Am I defining my scope incorrectly?

It seems as if the user is logged in with Auth::login($user); but then it does not actually authenticate.

Feb
14
3 months ago
Activity icon

Replied to Using Validation Rule Objects

Ah okay, so I'm just using the feature incorrectly in this scenario?

Activity icon

Replied to Using Validation Rule Objects

My validation is in a constant, so that I can re-use it.


const SUBMISSION_VALIDATION = [
    'email_type' => 'required|string|in:Work,Home,Personal,Other',
    'date_of_birth' => 'required|date|before:today|after:1900-01-01',
    'country_of_birth' => 'required|string',
    'town_of_birth' => 'required|string',
    'national_insurance_number' => 'required|string',
    'country_of_residency' => 'required|string',
    'tax_identification_number' => 'required|string',
    'politically_exposed_person' => 'required|boolean',
    'nationality' => 'required|string',
    'has_secondary_nationality' => 'required|boolean',
    'secondary_nationality' => 'nullable|string',
    'address_line_1' => 'required|string',
    'address_line_2' => 'nullable|string',
    'city' => 'required|string',
    'county' => 'required|string',
    'postcode' => 'required|string',
    'country' => 'required|string',
    'time_at_address' => 'required|array|size:2',
    'time_at_address.*' => 'required',
    'previous_address' => 'nullable|string',
    'telephone_type' => 'required|integer|in:1,2,3,4,5,6,7',
    'telephone_number' => 'required|string|min:10',
    'dialing_code' => 'required|integer',
    'bank_name' => 'required|string',
    'account_name' => 'required|string',
    'account_number' => 'required|string|alpha_num|max:34',
    'sort_code' => 'required|string|numeric|digits_between:1,11',
    'independent_financial_advisor' => 'required|boolean',
    'independent_financial_advisor_firm' => 'required_if:independent_financial_advisor,1|string|nullable',
    'independent_financial_advisor_name' => 'required_if:independent_financial_advisor,1|string|nullable',
    'independent_financial_advisor_telephone' => 'required_if:independent_financial_advisor,1|string|nullable',
    'independent_financial_advisor_email_address' => 'required_if:independent_financial_advisor,1|string|email|nullable',
    'independent_financial_advisor_address' => 'required_if:independent_financial_advisor,1|string|nullable',
    'confirm_information_memorandum' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'understand_objective' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'objectives_match_personal_objective' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'understand_term_held' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'career_history' => 'required_if:independent_financial_advisor,0|string|nullable|min_words:25',
    'investment_experience' => 'required_if:independent_financial_advisor,0|string|nullable|min_words:25',
    'confirm_high_risk' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'confirm_potential_loss_impact' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'confirm_loss_of_rights' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'confirm_no_compensation_rights' => 'required|boolean|true_if_reference_is_false:independent_financial_advisor',
    'tax_relief' => 'required|boolean',
];

So this is why I used Validator::extend as you can't use functions inside constants.

But, calling handle wasn't ruterning the text in the message() method of the Rule class, which is why I used a translation.

Activity icon

Replied to Using Validation Rule Objects

Will this not depend on the point at which the rule is called?

Activity icon

Replied to Using Validation Rule Objects

In the translation file I have:


 'min_words' => 'The :attribute must be at least :min_words',

Activity icon

Started a new Conversation Using Validation Rule Objects

I created a new Rule class called MinWords that checks whether an attribute contains x amount of words.

It looks like this:


<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class MinWords implements Rule
{
    /**
     * The minimum words that should be entered.
     *
     * @var int
     */
    public $min_words;

    /**
     * Create a new rule instance
     *
     * @param int $max_words
     */
    public function __construct($min_words = 25)
    {
        $this->min_words = $min_words;
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return str_word_count($value) >= $this->min_words;
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return trans('validation.min_words');
    }
}



In this video: https://laracasts.com/series/lets-build-a-forum-with-laravel/episodes/53

It is shown that you can extend the validator using a class like I've done below:


/**
 * Extend the validator to include a min:words rule
 */
Validator::extend('min_words', 'App\Rules\[email protected]');


But how do you pass the attribute in the constructor to the translation file?

Feb
12
3 months ago
Activity icon

Started a new Conversation Getting Data Between Date Ranges

In Laravel when you make a migration by default you generally get two timestamps: created_at and updated_at which is a nice to have.

In my application if I wanted to get say, all the users who registered in the last month individually, or say, a count of these users, how would that be constructed?

I ask because I read that a controller should try to stick to the CRUD methods, but I don't see how this is possible in such a situation.

Basically I'm thinking of having a stats controller for things like:

  • registrations
  • activities
  • etc

In such a scenario would it be more scale-able just to select a month and have something like the following:


$users = User::whereMonth('created_at', $request->get('month'))->get();

Or would you use a job and just collate stats automatically?

I'm feeling this is actually dead simple but I'm floundering.

Feb
07
3 months ago
Activity icon

Replied to Listeners For Similar Actions

I actually agree with your approach because then it doesn't matter whether I'm queuing or not.

Activity icon

Replied to Listeners For Similar Actions

What about something I've just recalled: shouldQueue


    /**
     * Determine whether the listener should be queued.
     *
     * @param  \App\Events\MembershipUpgraded$event
     * @return bool
     */
    public function shouldQueue(SendUserMembershipPaymentReceipt$event)
    {
        return is_null($event->voucher) ;
    }


Activity icon

Replied to Listeners For Similar Actions

Hi,

This makes sense, so do you think this is an acceptable approach?

I'll do as you've suggested and make the voucher nullable on the event.

Also, the voucher percentage discount can differ, so I'd probably have to explicitly check the value in the model.

Activity icon

Started a new Conversation Listeners For Similar Actions

In my application a User can have a membership type that is either Passive or Active. Generally users become Active after they pay a subscription fee.

In this case I'm not using Laravel Cashier as the user wanted explicitly not to have any auto renewals, given this I just have a subscription_ends_at attribute.

Anyway, when a user pays it triggers an event called UpgradedMembership which has a few accompanying listeners in my EventServiceProvider.


UpgradedMembership::class => [
    StoreMembershipPayment::class,
    UpgradeUserMembershipToActive::class,
    SendUserMembershipPaymentReceipt::class,
    SendAdminUserMembershipNotification::class,
],

This works great but there's a catch, user's can apply 100% discount vouchers and my chosen payment gateway doesn't support transactions less than 5 pence or whatever the equivalent is.

So, I had it set to trigger UpgradedMembership if a voucher is used, but I then only really need the following:


UpgradedMembership::class => [
    // StoreMembershipPayment::class,
    UpgradeUserMembershipToActive::class,
    // SendUserMembershipPaymentReceipt::class,
    SendAdminUserMembershipNotification::class,
],

In this scenario how could I avoid duplication?

I could also make a new event like MembershipUpdradedWithVoucher, but I'd then have duplicate listeners.

Jan
31
4 months ago
Activity icon

Replied to Relationship Is Null In Test

To be fair, this is the first app I'm properly testing and it's been very useful so far, mainly for refactoring.

Activity icon

Replied to Relationship Is Null In Test

Thanks for the feedback, I'm going to go back through and recheck everything.

verified - email verified certified - if you've done the certification

Ah, so you're thinking $request->user() instead?

$user->investorDetails->update(factory(InvestorDetail::class)->raw([...]));

Ah, so rather than updating a user, just make the factory with a user_id included?

Our approaches seem similar, so I'll go and check everything.

Activity icon

Replied to Relationship Is Null In Test

The view is this one:


@component('mail::message')
# Dear {{ $application->user->first_name, 'Investor' }},

Your investment application was submitted successfully, we'll get back to you once this application has been processed.

**Your application details**

@component('mail::table')
| &nbsp;        | &nbsp;        | 
| ------------- |:-------------:| 
| Date of submission      | {{ $application->created_at->format('d M Y') }}                           |
| Amount pledged  | £{{ $application->amount_pledged }}                                               | 
| Type of application  | {{ $application->type }}                                                     | 
| Deal referenced  | {{ $application->deal ? $application->deal->name : 'Not applicable' }} | 
@endcomponent

@component('mail::panel')
This is an automated email from an unattended mailbox
@endcomponent

@component('mail::subcopy')
@include('emails.partials.risk-warning')
@endcomponent

@endcomponent


and for the admin one, it's this:


@component('mail::message')
# Hi,

{{ $investmentApplication->user->full_name }} has submitted an investment application.

**Application details**

@component('mail::table')
| &nbsp;        | &nbsp;        | 
| ------------- |:-------------:| 
| Date of submission      | {{ $investmentApplication->created_at->format('d M Y') }}                           |
| Amount pledged  | £{{ $investmentApplication->amount_pledged }}                                               | 
| Type of application  | {{ $investmentApplication->type }}                                                     | 
| Deal referenced  | {{ $investmentApplication->deal ? $investmentApplication->deal->name : 'Not applicable' }} | 
@endcomponent

@component('mail::panel')
This is an automated email from an unattended mailbox
@endcomponent

@endcomponent

it's called $application in one because I pass it as application to that view.

Can I ask what you would do differently?

Activity icon

Replied to Relationship Is Null In Test

So, in theory, should it work?

Activity icon

Replied to Relationship Is Null In Test

Both attributes exist:


    /**
     * Get the user's full name.
     *
     * @return string
     */
    public function getFullNameAttribute()
    {
        return "{$this->first_name} {$this->last_name}";
    }

Activity icon

Replied to Relationship Is Null In Test

It's I think because it that test, it's triggering both listeners so the user object is null in both.


ApplicationSubmitted::class => [
    LogApplicationSubmitted::class,
    SendUserApplicationSubmittedNotification::class,
    SendAdminApplicationSubmittedNotification::class,
    GenerateInvestmentApplicationPDF::class,
],

Activity icon

Replied to Relationship Is Null In Test


1) Tests\Feature\FundApplicationTest::submitting_a_fund_application_sends_a_notification_to_the_user
Facade\Ignition\Exceptions\ViewException: Trying to get property 'full_name' of non-object (View: C:\xampp\htdocs\projects\newable\npi-portal-laravel\resources\views\emails\admin\investment-application-submitted.blade.php)

C:\xampp\htdocs\projects\newable\npi-portal-laravel\resources\views/emails/admin/investment-application-submitted.blade.php:4
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:43
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Engines\CompilerEngine.php:59
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\facade\ignition\src\Views\Engines\CompilerEngine.php:36
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:143
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:126
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:91
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Markdown.php:64
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:267
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:237
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:153
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Support\Traits\Localizable.php:19
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:160
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\SendQueuedMailable.php:52
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:32
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\Util.php:36
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:90
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:34
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\Container.php:590
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php:94
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php:98
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:83
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:85
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:59
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php:88
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\SyncQueue.php:45
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\Queue.php:44
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:180
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailer.php:276
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailer.php:231
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php:261
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Listeners\SendAdminApplicationSubmittedNotification.php:28
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Events\Dispatcher.php:366
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Events\Dispatcher.php:196
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php:482
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Controllers\User\FundController.php:61
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Controller.php:54
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php:45
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Route.php:219
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Route.php:176
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:681
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\FundApplicationPending.php:20
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\ApplicationCompleted.php:24
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\EligibleInvestor.php:33
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Auth\Middleware\EnsureEmailIsVerified.php:29
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Middleware\SubstituteBindings.php:41
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:43
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php:76
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Middleware\ShareErrorsFromSession.php:49
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Session\Middleware\StartSession.php:56
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse.php:37
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\EncryptCookies.php:66
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:683
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:658
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:624
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:613
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:170
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\barryvdh\laravel-debugbar\src\Middleware\InjectDebugbar.php:58
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php:21
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php:21
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\ValidatePostSize.php:27
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode.php:63
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\fideloper\proxy\src\TrustProxies.php:57
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:145
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:110
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:434
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:252
C:\xampp\htdocs\projects\newable\npi-portal-laravel\tests\Feature\FundApplicationTest.php:139

Caused by
ErrorException: Trying to get property 'full_name' of non-object

C:\xampp\htdocs\projects\newable\npi-portal-laravel\storage\framework\viewse371f9d3c458ba53431582c1994380e1f3c879.php:4
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Engines\PhpEngine.php:43
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Engines\CompilerEngine.php:59
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\facade\ignition\src\Views\Engines\CompilerEngine.php:36
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:143
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:126
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\View.php:91
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Markdown.php:64
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:267
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:237
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:153
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Support\Traits\Localizable.php:19
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:160
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\SendQueuedMailable.php:52
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:32
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\Util.php:36
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:90
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\BoundMethod.php:34
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Container\Container.php:590
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php:94
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Bus\Dispatcher.php:98
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:83
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:85
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\CallQueuedHandler.php:59
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\Jobs\Job.php:88
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\SyncQueue.php:45
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Queue\Queue.php:44
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailable.php:180
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailer.php:276
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Mail\Mailer.php:231
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Support\Facades\Facade.php:261
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Listeners\SendAdminApplicationSubmittedNotification.php:28
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Events\Dispatcher.php:366
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Events\Dispatcher.php:196
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\helpers.php:482
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Controllers\User\FundController.php:61
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Controller.php:54
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\ControllerDispatcher.php:45
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Route.php:219
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Route.php:176
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:681
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\FundApplicationPending.php:20
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\ApplicationCompleted.php:24
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\app\Http\Middleware\EligibleInvestor.php:33
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Auth\Middleware\EnsureEmailIsVerified.php:29
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Middleware\SubstituteBindings.php:41
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Auth\Middleware\Authenticate.php:43
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken.php:76
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\View\Middleware\ShareErrorsFromSession.php:49
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Session\Middleware\StartSession.php:56
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse.php:37
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Cookie\Middleware\EncryptCookies.php:66
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:683
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:658
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:624
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Routing\Router.php:613
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:170
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:130
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\barryvdh\laravel-debugbar\src\Middleware\InjectDebugbar.php:58
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php:21
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\TransformsRequest.php:21
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\ValidatePostSize.php:27
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode.php:63
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\fideloper\proxy\src\TrustProxies.php:57
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:171
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php:105
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:145
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php:110
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:434
C:\xampp\htdocs\projects\newable\npi-portal-laravel\vendor\laravel\framework\src\Illuminate\Foundation\Testing\Concerns\MakesHttpRequests.php:252
C:\xampp\htdocs\projects\newable\npi-portal-laravel\tests\Feature\FundApplicationTest.php:139

Activity icon

Replied to Relationship Is Null In Test


    /** @test */
    public function submitting_a_fund_application_sends_a_notification_to_the_user()
    {
        Notification::fake();

        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $application = [
            'user_id' => $user->id,
            'type' => InvestmentApplication::TYPE_FUND,
            'deal_id' => null,
            'amount_pledged' => 10000,
            'payment_method' => InvestmentApplication::PAYMENT_BANK_TRANSFER,
        ];

        $this->actingAs($user)
            ->post(route('user.application.fund.store'), $application)
            ->assertStatus(302);

        $this->assertDatabaseHas('investment_applications', [
            'user_id' => $user->id,
            'type' => $application['type'],
            'deal_id' => null,
            'amount_pledged' => $application['amount_pledged'],
            'payment_method' => $application['payment_method'],
        ]);

        Notification::assertSentTo($user, InvestmentApplicationSubmitted::class);
    }

It always says trying to get property first_name of non object, I'm guessing I've fluffed something, but it does work when tested in a real browser with mail trap.


@component('mail::message')
# Dear {{ $application->user->first_name, 'Investor' }},

Your investment application was submitted successfully, we'll get back to you once this application has been processed.

**Your application details**

@component('mail::table')
| &nbsp;        | &nbsp;        | 
| ------------- |:-------------:| 
| Date of submission      | {{ $application->created_at->format('d M Y') }}                           |
| Amount pledged  | £{{ $application->amount_pledged }}                                               | 
| Type of application  | {{ $application->type }}                                                     | 
| Deal referenced  | {{ $application->deal ? $application->deal->name : 'Not applicable' }} | 
@endcomponent

@component('mail::panel')
This is an automated email from an unattended mailbox
@endcomponent

@component('mail::subcopy')
**Risk warning:** Your capital is at risk. Investing in early stage companies involves risks including loss of capital, illiquidity, lack of dividends and dilution. Newable Ventures Limited does not give tax or investment advice. The availability of tax relief depends on individual investors’ circumstances, and on investee companies’ qualifying status, both of which may be subject to change. If you are in doubt about eligibility for tax reliefs or the tax treatment of your investment, you should seek independent tax advice.

Newable Ventures Limited (FRN 795277) is an Appointed Representative of Larpent Newton & Co Ltd. Larpent Newton & Co Ltd is authorised and regulated by the Financial Conduct Authority, FRN 141275. 
Neither Newable Capital Limited nor Newable Private Investing Limited are Authorised and Regulated by the Financial Conduct Authority.
@endcomponent

@endcomponent

Activity icon

Replied to Relationship Is Null In Test

Hello,

This was very interesting in terms of my production environment, I'm just not sure why the test still fails. I have taken your advice though, and eager loaded the relation.

Jan
30
4 months ago
Activity icon

Started a new Conversation Relationship Is Null In Test

In my application a User can make an InvestmentApplication. These models are related to each other like so:


/**
 * Get this user's applications
 *
 * @return void
 */
public function applications()
{
    return $this->hasMany(InvestmentApplication::class, 'user_id', 'id');
}


/**
 * Get the user associated with the investment application.
 */
public function user()
{
    return $this->belongsTo(User::class, 'user_id', 'id');
}

Here is the part in my ApplicationController responsible for making applications:


/**
 * Store a new investment application for investing into the fund
 *
 * @param StoreFundApplication $request
 *
 * @return void
 */
public function store(StoreFundApplication $request)
{
    $this->authorize('create', InvestmentApplication::class);

    $data = $request->validated() + [
        'type' => InvestmentApplication::TYPE_FUND,
        'status' => 'Pending',
    ];

    $application = auth()->user()->applications()->create($data);

    event(new ApplicationSubmitted($application));

    return redirect()->route('user.applications.show', [$application]);
}

This triggers ApplicationSubmitted which looks like this:


<?php

namespace App\Events;

use App\InvestmentApplication;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class ApplicationSubmitted
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * The instance of InvestmentApplication
     *
     * @var InvestmentApplication
     */
    public $investmentApplication;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(InvestmentApplication $investmentApplication)
    {
        $this->investmentApplication = $investmentApplication;
    }
}


This event has attached listeners:


ApplicationSubmitted::class => [
    LogApplicationSubmitted::class,
    SendUserApplicationSubmittedNotification::class,
    SendAdminApplicationSubmittedNotification::class,
    GenerateInvestmentApplicationPDF::class,
],

In SendUserApplicationSubmittedNotification I use $investmentApplication->user->first_name but in my test the user returns null.

My test:


<?php

namespace Tests\Feature;

use App\Events\ApplicationSubmitted;
use App\InvestorDetail;
use App\InvestmentApplication;
use App\Mail\Admin\UserApplicationSubmitted;
use App\Notifications\InvestmentApplicationSubmitted;
use App\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;

class FundApplicationTest extends TestCase
{
    use RefreshDatabase;

    /** @test */
    public function a_user_who_has_not_completed_their_investor_details_cannot_access_the_fund_application_form()
    {
        $user = factory(User::class)->create();

        $this->actingAs($user->fresh())
            ->get(route('user.application.fund.create'))
            ->assertStatus(302);
    }

    /** @test */
    public function a_user_who_has_completed_their_investor_details_can_access_the_fund_application_form()
    {
        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $this->actingAs($user)
            ->get(route('user.application.fund.create'))
            ->assertStatus(200);
    }

    /** @test */
    public function a_user_who_has_completed_their_investor_details_can_submit_a_fund_application()
    {
        $this->expectsEvents(ApplicationSubmitted::class);

        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $application = [
            'type' => InvestmentApplication::TYPE_FUND,
            'deal_id' => null,
            'amount_pledged' => 10000,
            'payment_method' => InvestmentApplication::PAYMENT_BANK_TRANSFER,
        ];

        $this->actingAs($user)
            ->post(route('user.application.fund.store'), $application)
            ->assertStatus(302);

        $this->assertDatabaseHas('investment_applications', [
            'user_id' => $user->id,
            'type' => $application['type'],
            'deal_id' => null,
            'amount_pledged' => $application['amount_pledged'],
            'payment_method' => $application['payment_method'],
        ]);
    }

    /** @test */
    public function submitting_a_fund_application_creates_an_activity_for_this_user()
    {
        Notification::fake();

        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $application = [
            'type' => InvestmentApplication::TYPE_FUND,
            'deal_id' => null,
            'amount_pledged' => 10000,
            'payment_method' => InvestmentApplication::PAYMENT_BANK_TRANSFER,
        ];

        $this->actingAs($user)
            ->post(route('user.application.fund.store'), $application)
            ->assertStatus(302);

        $this->assertDatabaseHas('investment_applications', [
            'user_id' => $user->id,
            'type' => $application['type'],
            'deal_id' => null,
            'amount_pledged' => $application['amount_pledged'],
            'payment_method' => $application['payment_method'],
        ]);

        $this->assertDatabaseHas('user_activities', [
            'user_id' => $user->id,
        ]);
    }

    /** @test */
    public function submitting_a_fund_application_sends_a_notification_to_the_user()
    {
        Notification::fake();

        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $application = [
            'user_id' => $user->id,
            'type' => InvestmentApplication::TYPE_FUND,
            'deal_id' => null,
            'amount_pledged' => 10000,
            'payment_method' => InvestmentApplication::PAYMENT_BANK_TRANSFER,
        ];

        $this->actingAs($user)
            ->post(route('user.application.fund.store'), $application)
            ->assertStatus(302);

        $this->assertDatabaseHas('investment_applications', [
            'user_id' => $user->id,
            'type' => $application['type'],
            'deal_id' => null,
            'amount_pledged' => $application['amount_pledged'],
            'payment_method' => $application['payment_method'],
        ]);

        Notification::assertSentTo($user, InvestmentApplicationSubmitted::class);
    }

    /** @test */
    public function submitting_a_fund_application_sends_a_notification_to_npi()
    {
        Mail::fake();

        $user = factory(User::class)->states('verified', 'certified', 'passive')->create();

        $user->investorDetails->update(factory(InvestorDetail::class)->raw([
            'user_id' => $user->id,
        ]));

        $this->assertTrue($user->investorDetails->is_completed);

        $application = [
            'user_id' => $user->id,
            'type' => InvestmentApplication::TYPE_FUND,
            'deal_id' => null,
            'amount_pledged' => 10000,
            'payment_method' => InvestmentApplication::PAYMENT_BANK_TRANSFER,
        ];

        $this->actingAs($user)
            ->post(route('user.application.fund.store'), $application)
            ->assertStatus(302);

        $this->assertDatabaseHas('investment_applications', [
            'user_id' => $user->id,
            'type' => $application['type'],
            'deal_id' => null,
            'amount_pledged' => $application['amount_pledged'],
            'payment_method' => $application['payment_method'],
        ]);

        Mail::assertSent(UserApplicationSubmitted::class, function ($mail) {
            return $mail->hasTo('[email protected]');
        });
    }
}

If I use $this->withOutExceptionHandling() I can do the following in the notification itself:


<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\User;

class InvestmentApplicationSubmitted extends Notification implements ShouldQueue
{
    use Queueable;

    /**
     * The investment application submitted
     *
     * @var InvestmentApplication
     */
    public $investmentApplication;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($investmentApplication)
    {
        $this->investmentApplication = $investmentApplication;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification.
     *
     * @param  mixed                                          $notifiable
     * @return \Illuminate\Notifications\Messages\MailMessage
     */
    public function toMail($notifiable)
    {
        dd($this->investmentApplication->user);
        dd(User::find($this->investmentApplication->user_id));

        return (new MailMessage)
            ->subject("{$notifiable->first_name}, your investment application was submitted")
            ->markdown('emails.user.investment-application-submitted', [
                'application' => $this->investmentApplication,
            ]);
    }
}

The line - dd($this->investmentApplication->user); returns null but the line underneath returns a valid User object.

I am unsure why the investmentApplication thinks it's related user is null, even though I'm posting the store route that uses the relationship to create the application in the first place.

Jan
27
4 months ago
Activity icon

Replied to Creating A Simple Activity Logger

My only thought is that I may not always have a model instance.

Jan
26
4 months ago
Activity icon

Replied to Creating A Simple Activity Logger

So in that sense I'd have a pivot table between user and activity?

Jan
24
4 months ago
Activity icon

Replied to Creating A Simple Activity Logger

Also, because the model can be null, should I do this?


    public function handle($event)
    {
        $event->user->activities()->create([
            'type' => $event->type,
            'description' => $event->description,
            'model_name' => get_class($event->model),
            'model_id' => $event->model ? $event->model->id : null,
        ]);
    }

Activity icon

Replied to Creating A Simple Activity Logger

Hi,

Do you think this approach is a little rudimentary? I just don't like over complicating for the sake of it.

Activity icon

Started a new Conversation Creating A Simple Activity Logger

In my application I need to be able to log a user activity in a table, so these can be viewed later.

Starting off, I made a migration:


<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateUserActivitiesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('user_activities', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->uuid('user_id')->index();
            $table->string('type');
            $table->longText('description');
            $table->unsignedBigInteger('model_id')->nullable();
            $table->string('model_type')->nullable();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('user_activities');
    }
}

Then I made a model:


<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class UserActivity extends Model
{
    /**
     * The table this model is related to
     *
     * @var string
     */
    protected $table = 'user_activities';

    /**
     * The attributes that are mass assignable
     */
    protected $fillable = [
        'user_id', 'type', 'description', 'model_type', 'model_id',
    ];

    /**
     * Get the user associated with this activity
     */
    public function user()
    {
        return $this->belongsTo(User::class, 'user_id');
    }
}

Then I made an event:


<?php

namespace App\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserActivityOccurred
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    /**
     * The user performing the activity
     *
     * @var User
     */
    public $user;

    /**
     * The type of activity that was performed
     *
     * @var string
     */
    public $type;

    /**
     * The description off the activity
     *
     * @var string
     */
    public $description;

    /**
     * The model involved
     *
     * @var string
     */
    public $model;

    /**
     * Creates a new event instance
     *
     * @param User   $user
     * @param string $type
     * @param string $description
     * @param mixed  $model
     */
    public function __construct($user, $type, $description, $model = null)
    {
        $this->user = $user;
        $this->type = $type;
        $this->description = $description;
        $this->model = $model;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('channel-name');
    }
}

Then a listener:


<?php

namespace App\Listeners;

class LogUserActivityOccurred
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
    }

    /**
     * Handle the event.
     *
     * @param  object $event
     * @return void
     */
    public function handle($event)
    {
        $event->user->activities()->create([
            'type' => $event->type,
            'description' => $event->description,
            'model_name' => '',
            'model_id' => '',
        ]);
    }
}

So, I then like to trigger an event wherever I want like so:


event(new UserActivityOccurred(auth->user()->id, 'Activity type',  'Description of activity', $model));

The part that baffles me, is the $model part. If I pass in $post, how could I get the namespace and ID?

This is because in future I may need to link activities to models.

I have seen many a package for this but I was curios.

Dec
02
6 months ago
Activity icon

Started a new Conversation Testing Within Model Observers

In my recent projects I've been experimenting with Observers and they seem like a great way to do things tied to model events.

In one model called KnowledgeBaseArticle I send an email to a particular group of users if the status is changed.


/**
 * Handle the knowledge base article "updated" event.
 *
 * @param  \App\KnowledgeBaseArticle $knowledgeBaseArticle
 * @return void
 */
public function updated(KnowledgeBaseArticle $knowledgeBaseArticle)
{
    Log::info("A knowledge base article was updated {$knowledgeBaseArticle->title} in knowledge base {$knowledgeBaseArticle->knowledge_base->name}");

    if ($knowledgeBaseArticle->isDirty('status') && $knowledgeBaseArticle->is_published) {
        // Send a mailable to all DIT members about the new DIT article
        if ($knowledgeBaseArticle->knowledge_base_slug === 'dit') {
            User::role('dit member')->get()
                ->each(function ($user) use ($knowledgeBaseArticle) {
                    Mail::to($user)
                        ->queue(new KnowledgeBaseArticleCreatedNotification($knowledgeBaseArticle));
                });
        }
    }
}

Another example if if an Article needs approval


/**
 * Handle the article "created" event.
 *
 * @param  \App\Article  $article
 * @return void
 */
public function created(Article $article)
{
    Log::notice("A new article was created: {$article->title}");

    // If the article needs publishing, send a notification
    if ($article->needs_approval) {
        // Get users with appropriate roles
        User::role(['admin', 'news administrator'])->get()
            ->each(function ($user) use ($article) {
                $user->notify((new ArticleCreated($article))->delay(now()->addSeconds(10)));
            });
    }
}

Another is generating a slug


    /**
     * Handle the article "creating" event.
     *
     * @param  \App\Article $article
     * @return void
     */
    public function creating(Article $article)
    {
        $article->slug = Str::slug($article->title);

    }

So, there are three examples where I've used observers to do something.

My question is: how would I test this?

I'd want to have something like so:


    /** @test */
    public function a_published_knowledge_base_article_sends_an_email()
    {
        // put request to knowledge-base.article.update
        // Data - status -> Published
        // Criteria met in updating event
        // Email is sent to queue
        // Assert test passes
    }


/** @test */
public function a_published_knowledge_base_article_sends_an_email()
{
    $this->withoutExceptionHandling();

    Mail::fake();

    $article = factory(KnowledgeBaseArticle::class)->create(['status' => 'Unpublished']);

    $attributes = [
        'title' => 'An update on Brexit',
        'status' => 'Published'
    ];

    $this->actingAs($this->admin)
            ->put(route('knowledge-base.article.update', $article), $attributes)
            ->assertStatus(302);

    Mail::assertQueued(KnowledgeBaseArticleCreatedNotification::class);
}