jesse_orange_newable

jesse_orange_newable

Member Since 1 Year Ago

Experience Points
7,770
Total
Experience

2,230 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,770 XP
Nov
24
4 days ago
Activity icon

Replied to Collection Each() Method Vs Model Accessors, Is One Better Than The Other?

Is there ever a scenario where being more specific with your query would actually be more costly?

I'm curious because on the Laracasts tutorials there isn't a great deal about general optimisation, I find its more about use the ORM more effectively.

Activity icon

Started a new Conversation Collection Each() Method Vs Model Accessors, Is One Better Than The Other?

Here is a job's handle method:


/**
 * Execute the job.
 *
 * @return void
 */
public function handle()
{
    Log::info('Checking for expired Tasks');

    $tasks = Task::get();
    
    foreach ($tasks as $task) {
        if ($task->incomplete() && $task->has_expired) {
            Log::notice("{$task->name} has expired");

            Notification::send(
                $task->assignee,
                new TaskExpiredNotification($task)
            );
        }
    }

    // Retrieve all tasks that have not been completed and should have been completed by today
    Task::whereNull('completed_at')
        ->whereDate('completed_by', '<', Carbon::today())
        ->get()
        ->each(function($task)){
            Log::notice("{$task->name} has expired");
            $task->assignee->notify(new TaskExpiredNotification($task));
        }
}

As you'll see one chunk gets the data then iterates through using accessors and methods to do various checks, the other just uses the each() method and some additional query constraints.

My question is, which is better, if this can be the case?

Is it better to grab everything and then iterate, or only grab what you need initially?

Activity icon

Replied to Coding A What You've Missed Type System

Thanks for your input, that makes sense. As you want them to be useful but at the same time you don't want to actively put off the user.

Nov
23
5 days ago
Activity icon

Started a new Conversation Coding A What You've Missed Type System

So this is aweird one to word, so bare with me.

I have a system where administrators can upload documents for a user and each document upload triggers an event like NewDocumentAvailable which makes sense at first glance.

Here's the method:


    /**
     * Store a new document for a given user
     *
     * @param \App\Http\Requests\StoreDocument $request
     */
    public function store(StoreDocument $request)
    {
        $data = $request->validated();

        $user = User::findOrFail($data['selected_user']);

        $file = $request->file;

        $user_path = 'users/' . $user->id;

        $file_name = sha1(date('YmdHis') . Str::random(45)) . '.' . $file->getClientOriginalExtension();

        $path = Storage::disk('documents')->putFileAs($user_path, $file, $file_name);

        $row_order = isset($user->documents()->orderBy('row_order', 'desc')->first()->row_order) ? $user->documents()->orderBy('row_order', 'desc')->first()->row_order + 1 : 0;

        $document = $user->documents()->create([
            'row_order' => $row_order,
            'name' => $file->getClientOriginalName(),
            'file_name' => $file_name,
            'size' => $file->getSize(),
            'extension' => strtolower($file->getClientOriginalExtension()),
            'disk' => 'documents',
            'path' => $path,
        ]);

        if ($document->user->document_available_notification) {
            $document->user->sendNewDocumentNotification($document);
        }

        return response()->json([
            'success' => true,
        ], 200);
    }

My thought on this though, is if I were to upload 6,000 documents, it would be stupid to send 6,000 emails, it'd be much better to say "Oh, hi user since x this has happened" or "Btw, you have new documents to view".

I know I could do this in a Job based on the created_at timestamp but how do I determine if something is truly new?

Also, if it were like two documents that'd be fine, but if there were thousands would it be better to have a job that just checks for new models since point x?

Sep
25
2 months ago
Activity icon

Started a new Conversation Laravel Pagination In Vue.js

I am using Laravel's paginate method with Vue.js for a load more button, the pagination in my Vue component is an object like so:


(this.pagination = {
  from: response.data.from,
  to: response.data.to,
  total: response.data.total,
  current_page: response.data.current_page,
  last_page: response.data.last_page,
  next_page_url: response.data.next_page_url,
  prev_page_url: response.data.prev_page_url,
}),

As you can see I'm just grabbing what the paginate method responds with.

Here is the base paginate method


    /**
     * Paginate the given query into a simple paginator.
     *
     * @param  int  $perPage
     * @param  string  $pageName
     * @param  int|null  $page
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function paginate($perPage = null, $pageName = 'page', $page = null)
    {
        $engine = $this->engine();

        $page = $page ?: Paginator::resolveCurrentPage($pageName);

        $perPage = $perPage ?: $this->model->getPerPage();

        $results = $this->model->newCollection($engine->map(
            $this, $rawResults = $engine->paginate($this, $perPage, $page), $this->model
        )->all());

        $paginator = (new LengthAwarePaginator($results, $engine->getTotalCount($rawResults), $perPage, $page, [
            'path' => Paginator::resolveCurrentPath(),
            'pageName' => $pageName,
        ]));

        return $paginator->appends('query', $this->query);
    }

In Vue how can I append or amend parameters?

I see it has a query parameter already, is it possible to add more?

Sep
24
2 months ago
Activity icon

Started a new Conversation Using Laravel Scout Or An SQL LIKE Operator

In one of my projects we have Articles and Events (singular) and I wanted the ability to search through them with a search bar.

Laravel Scout was great for this as I could literally do the following:


/**
    * Perform a search using what was typed into the search bar
    * Uses Laravel Scout for full text indexing.
    *
    * @param  Request $request
    * @return void
    */
public function search(Request $request)
{
    // Do some validation
    $validated_data = $request->validate([
        'q' => 'nullable|string|max:255',
    ]);

    // Get what was searched for
    $query_string = $validated_data['q'];

    $articles = Article::search($query_string)
        ->where('status', 'published')
        ->orderBy('created_at', 'desc')
        ->get();

    $events = Event::search($query_string)
        ->where('published', 'published')
        ->orderBy('start_datetime', 'desc')
        ->get();
    }

This worked fine.

One day users want tagging so I use https://cartalyst.com/manual/tags/11.x to handle the many-to-many aspects of this request.

So, users now want to be able to search and if it returns nothing, it should instead compare the search term to any available tag.

I've been reading up and I don't think scout would be a good choice at this point because essentially I want to use a bunch or orWhere to do the following:

  1. Search through each column of the model and try to match the string
  2. If we don't find anything, search through the tags

$articles = Article::search($query_string)
    ->where('status', 'published')
    ->orderBy('created_at', 'desc')
    ->get();

if(!$articles){
    $articles = Article::whereHas('tags', function($query){
        $query->where('name', 'like', '%' . $query_string . '%');
    })
}

Is there a better, more efficient way to do this?

Sep
04
2 months ago
Activity icon

Replied to What's The Best Way To Limit A Many To Many Relation?

I'm always over complicating these things....

Activity icon

Started a new Conversation What's The Best Way To Limit A Many To Many Relation?

In a project I'm working on I have a many to many relation between a Post and a Tag so going by Laravel convention there is a pivot called post_tag to hook everything up.

What would be the best way to limit the number of tags that could be set on the Post model?

My ideas are as follows:

  1. A pivot model observer to hook into the created event and check the count
  2. A validation rule that checks the size of the array to be synced

I think 2. is more likely as I could validate the size of the array but in practice would there be any performance hit on making a rule similar to exists that gets the model count?

As then I could compare the array size to the number of rows.

What do you guys and girls think?

Sep
02
2 months ago
Activity icon

Replied to Correct Way To Pass Multiple Parameters Of Same Type

This is an Asynchronous request using Axios within a Vue.js component.

Activity icon

Started a new Conversation Listener In Event Doesn't Know Related Objects

I have the following in my EventServiceProvider.php file.


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

As you can see the event happens and then the listeners kick in.

I have a test to verify this:


/** @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,
        'fund_agreement' => 1,
        'electronic_signature' => 1,
    ];

    $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,
    ]);
}

Inside LogApplicationSubmitted it is able to retrieve application->user just fine but then, in GenerateInvestmentApplicationPDF the same returns null.

<?php

namespace App\Listeners;

use App\Events\ApplicationSubmitted;
use App\Events\UserActivityOccurred;
use Log;

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

    /**
     * Handle the event.
     *
     * @param  ApplicationSubmitted $event
     * @return void
     */
    public function handle(ApplicationSubmitted $event)
    {
        $application = $event->investmentApplication;

        Log::info("An investment application of type {$application->type} was submitted with a pledged value of {$application->amount_pledged} by {$application->user->full_name}");

        event(new UserActivityOccurred(
            $application->user,
            'investment application',
            'submitted an investment application for  ' . $application->amount_pledged . ' via ' . $application->payment_method,
            $application->deal
        ));
    }
}



Then in the next listener


<?php

namespace App\Listeners;

use App\Events\ApplicationSubmitted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Carbon\Carbon;
use PDF;
use Storage;

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

    /**
     * Handle the event.
     *
     * @param  ApplicationSubmitted $event
     * @return void
     */
    public function handle(ApplicationSubmitted $event)
    {
        $application = $event->investmentApplication;

        dd($application->user); // returns null

        $directory = "applications/{$application->user_id}";

        $filename = "{$application->name} Application - " . Carbon::now()->format('d-m-y-H-i-s') . '.pdf';

        $path = $directory . '/' . $filename;

        // Generate a PDF from the given view
        $pdf = PDF::loadView('pdf.user.investment-application', compact('application', 'user'))
            ->setPaper('a4', 'landscape');

        // Save this PDF to storage
        Storage::disk('local')->put($path, $pdf->output());

        // Update the investment application with the path to the newly created file
        $event->investmentApplication->update([
            'pdf_path' => $path,
        ]);
    }
}


Activity icon

Replied to Correct Way To Pass Multiple Parameters Of Same Type

I didn't realise there was a limit to be honest, although that's good to know. I'give the video and watch and update my question.

Aug
28
3 months ago
Activity icon

Started a new Conversation Correct Way To Pass Multiple Parameters Of Same Type

I have a page where users can filter by multiple tags in order to amend a search using Laravel scout.


    $search_term = $request->get('q');
    $tags = $request->get('tags');

    // Organise tags from request into usable array
    $tags = collect($tags)->map(function ($item, $key) {
        return [
            json_decode($item)->slug,
        ];
    })->flatten()->toArray();

    // Build error message if no results are found
    $error = ['error' => "No results found for '{$request->get('q')}'"];
    
    $error['error'] = count($tags) > 0
        ? $error['error'] . ' with ' . count($tags) . ' tag(s) selected.'
        : $error['error'];

    // If there is a search term, search for events meeting search criteria
    if (!empty($search_term)) {
        $events = Event::search($search_term)->query(function ($query) use ($tags) {
            return $query->listed()
                ->when(count($tags) > 0, function ($query) use ($tags) {
                    return $query->withTag($tags);
                })
                ->select('id', 'title', 'header_image_url', 'location', 'start_datetime', 'finish_datetime');
        })->paginate(6);
    }
    // If there is NO search term, load all listed events meeting tag criteria
    else {
        $events = Event::listed()
            ->withTag($tags)
            ->select('id', 'title', 'header_image_url', 'location', 'start_datetime', 'finish_datetime')
            ->orderBy('start_datetime')
            ->paginate(6);
    }

    return $events->count() ? $events : $error;
}


Where tags is an array of selections like blue, red, green, however the URLs become really odd:


Request URL: http://127.0.0.1:8000/api/events/search?q=&tags[]=%7B%22id%22:3,%22name%22:%22Find+International+Partners%22,%22slug%22:%22find-international-partners%22,%22checked%22:true%7D&tags[]=%7B%22id%22:5,%22name%22:%22Grow+in+the+US%22,%22slug%22:%22grow-in-the-us%22,%22checked%22:true%7D&tags[]=%7B%22id%22:14,%22name%22:%22Innovate+UK%22,%22slug%22:%22innovate-uk%22,%22checked%22:true%7D&tags[]=%7B%22id%22:7,%22name%22:%22Investment+Pitch%22,%22slug%22:%22investment-pitch%22,%22checked%22:true%7D&tags[]=%7B%22id%22:15,%22name%22:%22JP+Morgan%22,%22slug%22:%22jp-morgan%22,%22checked%22:true%7D&tags[]=%7B%22id%22:18,%22name%22:%22Newable%22,%22slug%22:%22newable%22,%22checked%22:true%7D

and then when I try to paginate, trying to append that is horrible.

Should it just be like so?

tags=tag1&&tag2&&tag3 rather than entire objects?

Aug
06
3 months ago
Activity icon

Replied to Working With Date And Time

My bad, I forgot a part:


/**
 * Set the start date & time
 *
 * @param [type] $value
 *
 * @return void
 */
public function setStartDatetimeAttribute($value)
{
    $date = $value['date'] ?? null;
    $time = $value['time'] ?? '';

    if (!is_null($date)) {
        return $this->attributes['start_datetime'] = Carbon::parse($date . $time);
    }

    return $this->attributes['start_datetime'] = Carbon::now();
}

Activity icon

Started a new Conversation Working With Date And Time

I have a query scope called inDate that is meant to check the given start_datetime and check whether its newer than today i.e Carbon::today(), however I can't wrap my head around this.

I have a test:

/** @test */
public function it_knows_if_its_in_date_query_scope()
{
    $start = Carbon::now();
    $end = Carbon::now()->addDay();

    $eventInDate = factory(Event::class)->create([
        'date_not_available' => false,
        'start_datetime' => [
            'date' => $start->format('Y-m-d'),
            'time' => $start->format('H:i'),
        ],
        'finish_datetime' => [
            'date' => $end->format('Y-m-d'),
            'time' => $end->format('H:i'),
        ],
    ]);

    $eventsDueToHappen = Event::inDate()->get();

    $this->assertFalse($eventsDueToHappen->contains($eventInDate));
}


Now, the start_datetime of dueToHappen is the current time, which for me is 2020-08-06 16:59:00

The query scope looks like this:


public function scopeInDate($query)
{
    return $query->where('start_datetime', '>=', Carbon::today()->startOfDay()->toDateTimeString());
}

Dumping this outputs the following SQL:


"select * from `events` where `start_datetime` >= ? and `events`.`deleted_at` is null"
array:1 [
  0 => "2020-08-06 00:00:00"
]

How can this test be failing as 16:00 is older than 00:00

Jul
01
4 months ago
Activity icon

Replied to Fixing Ugly Code

Hi,

Essentially, users fill in a web form and then this populates a template within DocuSign. Each model has a few envelope specific fields that I've added below.


$table->string('envelope_id')->nullable();
$table->string('envelope_status')->nullable();
$table->timestamp('envelope_sent_at')->nullable();
$table->timestamp('envelope_signed_at')->nullable();
$table->timestamp('envelope_voided_at')->nullable();
$table->string('envelope_completed_document_path')->nullable();

I feel these are fairly straightforward, literally just fields to hold information on the envelope that was sent.


// Retrieve the unsigned envelopes from the company registrations
        $actionPlanEnvelopes = CompanyActionPlan::select('envelope_id')
            ->unsignedEnvelope()
            ->unvoidedEnvelope()
            ->pluck('envelope_id')
            ->toArray();

This just says only bother checking models that have an envelope that hasn't already been voided or signed.


    /**
     * Check the action plan forms against the returned changes from DocuSign
     *
     * @param string $envelope
     * @param mixed  $client
     *
     */
    private function checkActionPlans($envelope, $client)
    {
        $actionPlan = CompanyActionPlan::where('envelope_id', $envelope['envelope_id'])->first();

        if ($actionPlan) {
            // If the document has been voided within DocuSign update our local record
            if ($envelope['status'] == 'voided') {
                Log::info("Action plan for company {$actionPlan->company_name} was voided.");
                $voidedAt = Carbon::parse($envelope['voided_date_time']);
                $actionPlan->markAsVoided($voidedAt);
                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }

            // If the document has been declined
            if ($envelope['status'] == 'declined') {
                Log::info("Action plan for company {$actionPlan->company_name} was declined.");
                $actionPlan->markAsDeclined();
                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }

            // If the document has been delivered
            if ($envelope['status'] == 'delivered') {
                Log::info("Action plan for company {$actionPlan->company_name} was delivered to the signer.");
                $actionPlan->markAsDelivered();
            }

            // If the envelope is complete, timestamp it, notify the creator and grab the signed document
            if ($envelope['status'] == 'completed') {
                Log::info("Action plan for company {$actionPlan->company_name} was completed.");
                $completedAt = Carbon::parse($envelope['status_changed_date_time']);

                $filePath = "/action-plans/{$actionPlan->id}/signed.pdf";

                $signedDocument = $client->envelopes->getDocument(1, $actionPlan->envelope_id);

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

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

                $actionPlan->markAsCompleted($completedAt, $filePath);

                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }
        }
    }


In this section I get the envelope status and then update some fields and send a notification. On creation I also download the signed envelope.

The main issue I had was that the actual form fields were different so I just made different models. In truth they are all fairly similar.

Activity icon

Started a new Conversation Fixing Ugly Code

Hi everyone,

I have been working with the DocuSign e Signature API to send out documents to clients, then to check for changes I have been using polling.

I have a job to accomplish this:


<?php

namespace App\Jobs;

use App\CompanyActionPlan;
use App\CompanyOutcome;
use App\GrowthHubOutcome;
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\Notifications\ActionPlanStatusUpdated;
use App\Notifications\OutcomesFormStatusUpdated;
use App\Notifications\RegistrationFormStatusUpdated;
use DocuSign;
use Carbon\Carbon;
use Storage;
use Log;

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

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

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

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

        // Create an empty array
        $envelopeIds = [];

        // Retrieve the unsigned envelopes from the company registrations
        $actionPlanEnvelopes = CompanyActionPlan::select('envelope_id')
            ->unsignedEnvelope()
            ->unvoidedEnvelope()
            ->pluck('envelope_id')
            ->toArray();

        // Retrieve the unsigned envelopes from the action plans
        $registrationEnvelopes = CompanyRegistration::select('envelope_id')
            ->unsignedEnvelope()
            ->unvoidedEnvelope()
            ->pluck('envelope_id')
            ->toArray();

        // Retrieve the unsigned envelopes from the outcomes
        $outcomeEnvelopes = CompanyOutcome::select('envelope_id')
            ->unsignedEnvelope()
            ->unvoidedEnvelope()
            ->pluck('envelope_id')
            ->toArray();

        // Retrieve the unsigned envelopes from the lgh outcomes
        $growthHubOutcomeEnvelopes = GrowthHubOutcome::select('envelope_id')
            ->unsignedEnvelope()
            ->unvoidedEnvelope()
            ->pluck('envelope_id')
            ->toArray();

        // Merge the collection and send all envelope IDs to DocuSign for checking
        $envelopeIds = array_merge($actionPlanEnvelopes, $registrationEnvelopes, $outcomeEnvelopes, $growthHubOutcomeEnvelopes);

        // Only do anything if there are envelopes
        if ($envelopeIds) {
            $options = $client->envelopes->listStatusChangesOptions([
                'count' => 25,
                'envelope_ids' => $envelopeIds,
            ]);

            $changes = $client->envelopes->listStatusChanges($options);

            foreach ($changes['envelopes'] as $envelope) {
                $this->checkActionPlans($envelope, $client);
                $this->checkRegistrations($envelope, $client);
                $this->checkOutcomes($envelope, $client);
                $this->checkGrowthHubOutcomes($envelope, $client);
            }
        }
    }

    /**
     * Check the action plan forms against the returned changes from DocuSign
     *
     * @param string $envelope
     * @param mixed  $client
     *
     */
    private function checkActionPlans($envelope, $client)
    {
        $actionPlan = CompanyActionPlan::where('envelope_id', $envelope['envelope_id'])->first();

        if ($actionPlan) {
            // If the document has been voided within DocuSign update our local record
            if ($envelope['status'] == 'voided') {
                Log::info("Action plan for company {$actionPlan->company_name} was voided.");
                $voidedAt = Carbon::parse($envelope['voided_date_time']);
                $actionPlan->markAsVoided($voidedAt);
                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }

            // If the document has been declined
            if ($envelope['status'] == 'declined') {
                Log::info("Action plan for company {$actionPlan->company_name} was declined.");
                $actionPlan->markAsDeclined();
                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }

            // If the document has been delivered
            if ($envelope['status'] == 'delivered') {
                Log::info("Action plan for company {$actionPlan->company_name} was delivered to the signer.");
                $actionPlan->markAsDelivered();
            }

            // If the envelope is complete, timestamp it, notify the creator and grab the signed document
            if ($envelope['status'] == 'completed') {
                Log::info("Action plan for company {$actionPlan->company_name} was completed.");
                $completedAt = Carbon::parse($envelope['status_changed_date_time']);

                $filePath = "/action-plans/{$actionPlan->id}/signed.pdf";

                $signedDocument = $client->envelopes->getDocument(1, $actionPlan->envelope_id);

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

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

                $actionPlan->markAsCompleted($completedAt, $filePath);

                $actionPlan->user->notify(new ActionPlanStatusUpdated($actionPlan));
            }
        }
    }

    /**
     * Check the registration forms against the returned changes from DocuSign
     *
     * @param string $envelope
     * @param mixed  $client
     *
     */
    private function checkRegistrations($envelope, $client)
    {
        $registration = CompanyRegistration::where('envelope_id', $envelope['envelope_id'])->first();

        if ($registration) {
            if ($envelope['status'] == 'voided') {
                Log::info("Registration for company {$registration->company_name} was voided.");
                $voidedAt = Carbon::parse($envelope['voided_date_time']);
                $registration->markAsVoided($voidedAt);
                $registration->user->notify(new RegistrationFormStatusUpdated($registration));
            }

            // If the document has been declined
            if ($envelope['status'] == 'declined') {
                Log::info("Registration for company {$registration->company_name} was declined.");
                $registration->markAsDeclined();
                $registration->user->notify(new RegistrationFormStatusUpdated($registration));
            }

            // If the document has been delivered
            if ($envelope['status'] == 'delivered') {
                Log::info("Registration for company {$registration->company_name} was delivered to the signer.");
                $registration->markAsDelivered();
            }

            // If the envelope is complete, timestamp it, notify the creator and grab the signed document
            if ($envelope['status'] == 'completed') {
                Log::info("Registration for company {$registration->company_name} was completed.");
                $completedAt = Carbon::parse($envelope['status_changed_date_time']);

                $filePath = "/registrations/{$registration->id}/signed.pdf";

                $signedDocument = $client->envelopes->getDocument(1, $registration->envelope_id);

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

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

                $registration->markAsCompleted($completedAt, $filePath);

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

    /**
     * Check the registration forms against the returned changes from DocuSign
     *
     * @param string $envelope
     * @param mixed  $client
     *
     */
    private function checkOutcomes($envelope, $client)
    {
        $outcome = CompanyOutcome::where('envelope_id', $envelope['envelope_id'])->first();

        if ($outcome) {
            if ($envelope['status'] == 'voided') {
                Log::info("Outcome for company {$outcome->company_name} was voided.");
                $voidedAt = Carbon::parse($envelope['voided_date_time']);
                $outcome->markAsVoided($voidedAt);
                $outcome->user->notify(new OutcomesFormStatusUpdated($outcome));
            }

            // If the document has been declined
            if ($envelope['status'] == 'declined') {
                Log::info("Outcome for company {$outcome->company_name} was declined.");
                $outcome->markAsDeclined();
                $outcome->user->notify(new OutcomesFormStatusUpdated($outcome));
            }

            // If the document has been delivered
            if ($envelope['status'] == 'delivered') {
                Log::info("Outcome for company {$outcome->company_name} was delivered to the signer.");
                $outcome->markAsDelivered();
            }

            // If the envelope is complete, timestamp it, notify the creator and grab the signed document
            if ($envelope['status'] == 'completed') {
                Log::info("Outcome for company {$outcome->company_name} was completed.");
                $completedAt = Carbon::parse($envelope['status_changed_date_time']);

                $filePath = "/outcomes/{$outcome->id}/signed.pdf";

                $signedDocument = $client->envelopes->getDocument(1, $outcome->envelope_id);

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

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

                $outcome->markAsCompleted($completedAt, $filePath);

                $outcome->user->notify(new OutcomesFormStatusUpdated($outcome));
            }
        }
    }

    /**
     * Check the lgh outcomes forms against the returned changes from DocuSign
     *
     * @param string $envelope
     * @param mixed  $client
     *
     */
    private function checkGrowthHubOutcomes($envelope, $client)
    {
        $outcome = GrowthHubOutcome::where('envelope_id', $envelope['envelope_id'])->first();

        if ($outcome) {
            dd($envelope);
            if ($envelope['status'] == 'voided') {
                Log::info("Growth Hub Outcome for company {$outcome->company_name} was voided.");
                $voidedAt = Carbon::parse($envelope['voided_date_time']);
                $outcome->markAsVoided($voidedAt);
            }

            // If the document has been declined
            if ($envelope['status'] == 'declined') {
                Log::info("Growth Hub Outcome for company {$outcome->company_name} was declined.");
                $outcome->markAsDeclined();
            }

            // If the document has been delivered
            if ($envelope['status'] == 'delivered') {
                Log::info("Growth Hub Outcome for company {$outcome->company_name} was delivered to the signer.");
                $outcome->markAsDelivered();
            }

            // If the envelope is complete, timestamp it, notify the creator and grab the signed document
            if ($envelope['status'] == 'completed') {
                Log::info("Growth Hub Outcome for company {$outcome->company_name} was completed.");
                $completedAt = Carbon::parse($envelope['status_changed_date_time']);

                $filePath = "/outcomes/growth-hub/{$outcome->id}/signed.pdf";

                $signedDocument = $client->envelopes->getDocument(1, $outcome->envelope_id);

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

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

                $outcome->markAsCompleted($completedAt, $filePath);
            }
        }
    }
}


At the moment I have a private method to check each model type BUT they literally all basically do the same thing:

  • Check a status
  • Call a function
  • Send an email

Is this readable, and also, is there a better way?

I just feel like there's so much repetition.

Any suggestions?