madsem

madsem

Member Since 4 Years Ago

Experience Points
11,650
Total
Experience

3,350 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
77
Lessons
Completed
Best Reply Awards
1
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 3
11,650 XP
Oct
03
3 weeks ago
Activity icon

Replied to Test Passes Locally, But Fails During CI (GH Actions)

So this happens (I think), because phpunit (When refreshing database), and also GH actions workflow somehow have a different db session when trying to select from the temporary table. As these tables only exist during a session and are then destroyed.

Is there any way to force phpunit to keep using the same session?

Activity icon

Started a new Conversation Test Passes Locally, But Fails During CI (GH Actions)

I'm having a curious case of a feature test here.

During an import of a large csv file, a temporary mysql table is created. The csv file is loaded into the tmp table using LOAD DATA LOCAL INFILE.

Then, using a raw Insert ON DUPLICATE KEY UPDATE query, the data from the temporary table is written into the final models table.

The tests are passing local (funnily though only when I'm using the DatabaseMigrations trait, it fails also locally when I use RefreshDatabase).

On Github Actions during my CI flow, it always fails though... I honestly have no more idea why this happens, hope someone can point me in the right direction here :)

Here is the code:

The part in my importer class that triggers the above mentioned process:

       //  load csv into tmp table.
        $tmpTable = $this->load($this->pending->file, self::Columns);

        // insert from temporary table into criteria_performance_reports, update duplicate keys.
        $target = with(new CriteriaPerformanceSummaryReport)->getTable();
        $this->query->execute($target, $tmpTable, self::Columns);

first, the tmp table is created, then loadData executes the LOAD DATA... query.

public function load(string $file, array $columns): string
    {
        $table = $this->createTemporaryTable($columns);
        $this->loadData($file, $table, $columns);

        return $table;
    }
private function createTemporaryTable($columns)
    {
        $now = Carbon::now();
        $table = 'temporary_'
            .strtolower(Str::random(10))
            .'_'
            .$now->microsecond
            .'_'
            .$now->format('Y_M_d');

        Schema::connection($this->connection)
              ->create($table, function ($table) use ($columns) {
                  foreach ($columns as $column) {
                      $table->string($column)->nullable();
                  }

                  $table->temporary();
              });

        return $table;
    }
public function execute(string $targetTable, string $fromTable, array $columns): bool
    {
        $targetColumns = implode(',', $columns);
        $values = 't.'.implode(',t.', $columns);

        $update = array_combine(
            explode(',', $targetColumns),
            explode(',', $values)
        );
        $update = http_build_query($update, '', ',');

        $sql = "INSERT INTO $targetTable ($targetColumns) ".
            "SELECT $values ".
            "FROM $fromTable t ".
            "ON DUPLICATE KEY UPDATE $update";

        return DB::statement($sql);
    }

The code works, it's all imported etc. The problem really is that this test fails (Locally it only fails if used with RefreshDatabase trait), on GH actions it always fails:

public function it_imports_keyword_performance_report()
    {
        $file = $this->remoteService->get('KeywordPerformanceTest.csv');

        PendingFile::factory()->create([
            'file' => $file,
            'importable' => CriteriaPerformanceSummaryReportImportable::class,
        ]);

        $this->artisan('import-pending-files')
             ->assertExitCode(0)
             ->run();

        $this->assertNotNull(CriteriaPerformanceSummaryReport::first());
    }

Result:

There was 1 failure:
17

18
1) Tests\Feature\Console\Commands\ImportPendingFilesCommandTest::it_imports_keyword_performance_report
19
Failed asserting that null is not null.
Sep
27
1 month ago
Activity icon

Awarded Best Reply on How To Work With Model Statuses From Multiple Third-Party Sources

For anyone having a similar problem, the answer is: Single Table Inheritance

https://stagerightlabs.com/blog/single-table-inheritance/

Activity icon

Replied to How To Work With Model Statuses From Multiple Third-Party Sources

For anyone having a similar problem, the answer is: Single Table Inheritance

https://stagerightlabs.com/blog/single-table-inheritance/

Sep
21
1 month ago
Activity icon

Started a new Conversation How To Work With Model Statuses From Multiple Third-Party Sources

If need to integrate several third-party APIS that will all be queried under the same local Model.

One API could return Inactive instead of Paused and so forth, so I was thinking maybe it's a good idea to create ENUM Classes for each API that map their status to an app internal status.

But that would make things like working with bulk data harder/impossible (Think of insertion of Bulk Data from an API, I would always need to loop over all Models to map statuses instead of writing raw DB bulk queries where possible).

How would you solve saving statuses for things like Active, Paused, Suspended etc. that come from each API?

Sep
17
1 month ago
Activity icon

Replied to Tests Individually Green, Run Together Red... Ideas?

Alright, as so often, you post and then all of a sudden a lightbulb goes off and you find the answer :) I shouldn't check for find(1), cause 1 doesn't exist anymore in test #2 haha, better to be explicit.

Activity icon

Started a new Conversation Tests Individually Green, Run Together Red... Ideas?

Hey Guys,

Can anybody tell me why these tests turn green when run individually, but when run together in sequence the last one (fallback), turns red?

<?php

namespace Tests\Feature\Console\Commands;

use Tests\TestCase;
use Mockery\MockInterface;
use App\Models\ExchangeRate;
use Illuminate\Support\Facades\File;
use App\Clients\Support\ExchangeRateClient;
use Illuminate\Foundation\Testing\RefreshDatabase;

/**
 * @see \App\Console\Commands\DownloadExchangeRatesCommand
 */
class DownloadExchangeRatesCommandTest extends TestCase
{

    use RefreshDatabase;

    /**
     * @test
     */
    public function it_saves_exchange_rate_model_from_primary_endpoint()
    {
        $this->partialMock(ExchangeRateClient::class, function (MockInterface $mock) {

            $response = File::get(base_path('tests/__Fixtures__/ExchangeRateApi/exchangeratesapi.io.success.json'));

            $mock->shouldReceive('getLatestRates->queryEndpoint->collectResponse')
                 ->with($response);
        });

        $this->artisan('download-exchange-rates')
             ->assertExitCode(0)
             ->run();

        $this->assertNotNull(ExchangeRate::find(1));
    }


    /**
     * @test
     */
    public function it_saves_exchange_rate_model_from_fallback_endpoint()
    {
        $this->partialMock(ExchangeRateClient::class, function (MockInterface $mock) {

            $response = File::get(base_path('tests/__Fixtures__/ExchangeRateApi/frankfurter.app.success.json'));

            $mock->shouldReceive('getLatestRates->queryEndpoint->collectResponse')
                 ->with($response);
        });

        $this->artisan('download-exchange-rates')
             ->assertExitCode(0)
             ->run();

        $this->assertNotNull(ExchangeRate::find(1));
    }

}

PHPUnit 9.3.10 by Sebastian Bergmann and contributors.

Failed asserting that null is not null. /opt/project/tests/Feature/Console/Commands/DownloadExchangeRatesCommandTest.php:58

Aug
14
2 months ago
Activity icon

Started a new Conversation How To Solve Needing An Id In Subquery, Error: SELECT List Is Not In GROUP BY Clause And Contains Nonaggregated Column

Trying to aggregate a statistics table and group it by day, but I'm again and again running into the group by error.

What I am trying to do, is to push all the heavy lifting onto the DB layer and get a single result per reporting dimension & day.

What I want is currently only possible if I set database.php -> strict_mode => false. But I would like to make it work "the right way".

Here is the result I'd like to receive: (strict mode = false)

array:1 [
  0 => {#2161
    +"day": "2020-08-14"
    +"visits": 7
    +"impressions": "42"
    +"spend": "2790"
    +"time_on_site": "131717.1429"
    +"clicks": 2
    +"conversions": "2"
    +"revenue": "730"
  }
]

Here is my query code:

$query = DB::table('landing_page_events', 'events')
                   ->selectRaw('day')
                   ->selectRaw('count(day) as visits')
                   ->selectRaw('sum(page_views) as impressions')
                   ->selectRaw('sum(spend_in_base_currency) as spend')
                   ->selectRaw('avg(timestampdiff(second, page_load, page_close)) as time_on_site')
                   ->addSelect([
                       'clicks' => function ($sub) {
                           $sub->selectRaw('count(id)')
                               ->from('landing_page_offer_events')
                               ->whereColumn('landing_page_event_id', 'events.id');
                       },
                       'conversions' => function ($sub) {
                           $sub->selectRaw('sum(conversions)')
                               ->from('landing_page_offer_events')
                               ->whereColumn('landing_page_event_id', 'events.id');
                       },
                       'revenue' => function ($sub) {
                           $sub->selectRaw('sum(revenue_in_base_currency)')
                               ->from('landing_page_offer_events')
                               ->whereColumn('landing_page_event_id', 'events.id');
                       },
                   ])
                   ->where('property_id', $this->lander->property_id)
                   ->whereDate('day', now()->toDateString())
                   ->whereNotNull('visitor_processed_at')
                   ->whereNull('bot_detected_at')
                   ->groupBy('day')
                   ->cursor();

The problem is in the sub queries, because I need to aggregate everything from there that has the same id. But the main query is not functionally dependent on the id's.

So if I would include the id in the select, and in the group by it would work. But the issue is then that this is a table that can tens-of-thousands of results and I would get HUGE arrays back that I would then need to merge in php.

Is there any way how I can get the single aggregated result I need, without having to disable Mysql strict mode?

Aug
12
2 months ago
Activity icon

Replied to Possible To Combine Very Similar Queries To Receive A Grouped Collection?

I think we have our cables crossed haha, I try to explain it better :)

What I am trying to do is to get grouped results, for each of my individual dimensions. But if possible run that in a single query, using union all, joins, or however it could be possible.

So I'm not trying to simply query all dimensions at once and implode an array, but to receive results that are grouped by each individual dimension.

Something along those lines:

<?php

Illuminate\Support\Collection ^ {#2163
    'countries' => [
        #items: array:3 [
        0 => {#2151
        +"campaign_tracking_id": "28ec6fbb-8707-4def-b1c4-a154b23bafa1"
        + "day": "2020-08-12"
        + "time_on_site": "111202.7273"
        + "impressions": "57"
        + "visits": 11
        + "clicks": 4
        + "conversions": "1"
        + "revenue": "230"
    }
    1 => {#2162
        +"campaign_tracking_id": "7d232f7c-05df-4a4a-bfcf-1be4b953574b"
        + "day": "2020-08-12"
        + "time_on_site": "144425.5000"
        + "impressions": "10"
        + "visits": 2
        + "clicks": 0
        + "conversions": null
        + "revenue": null
    }
    2 => {#2169
        +"campaign_tracking_id": "7c2246b7-8b37-4366-90e2-86bb37601343"
        + "day": "2020-08-12"
        + "time_on_site": "275949.0000"
        + "impressions": "9"
        + "visits": 1
        + "clicks": 0
        + "conversions": null
        + "revenue": null
    }
  ],
    'devices' => [
        #items: array:4 [
        0 => {#2151
        +"campaign_tracking_id": "28ec6fbb-8707-4def-b1c4-a154b23bafa1"
        + "device_type": "Apple iPhone"
        + "day": "2020-08-12"
        + "time_on_site": "111202.7273"
        + "impressions": "57"
        + "visits": 32
        + "clicks": 4
        + "conversions": "1"
        + "revenue": "230"
    }
    1 => {#2151
        +"campaign_tracking_id": "28ec6fbb-8707-4def-b1c4-a154b23bafa1"
        + "device_type": "Sony Xperia"
        + "day": "2020-08-12"
        + "time_on_site": "111202.7273"
        + "impressions": "57"
        + "visits": 32
        + "clicks": 4
        + "conversions": "1"
        + "revenue": "230"
    }
    2 => {#2162
        +"campaign_tracking_id": "7d232f7c-05df-4a4a-bfcf-1be4b953574b"
        + "device_type": "Apple iPhone"
        + "day": "2020-08-12"
        + "time_on_site": "144425.5000"
        + "impressions": "10"
        + "visits": 2
        + "clicks": 0
        + "conversions": null
        + "revenue": null
    }
    3 => {#2169
        +"campaign_tracking_id": "7c2246b7-8b37-4366-90e2-86bb37601343"
        + "device_type": "Apple iPhone"
        + "day": "2020-08-12"
        + "time_on_site": "275949.0000"
        + "impressions": "9"
        + "visits": 1
        + "clicks": 0
        + "conversions": null
        + "revenue": null
    }
  ],
    ...
    ... 
    ...
}

I'm just unsure if that is technically even possible, or if I need to use single queries as in your examples.

Activity icon

Replied to Possible To Combine Very Similar Queries To Receive A Grouped Collection?

Hey @geordiejackson thanks a lot. I might've phrased it a little wonky, but I'm trying to (if that is possible) only a single query to get results for all dimensions in one go.

If I don't completely misunderstand your answer, this would just run in a loop and save code, but still run one query per dimension, correct?

Activity icon

Started a new Conversation Possible To Combine Very Similar Queries To Receive A Grouped Collection?

Hey there,

I'm writing query builder queries, against a single table to aggregate the data in there, by different reporting dimensions.

If at all possible, I'd like to run a single query and receive output grouped by the dimensions.

This is what I have:

$query = DB::table('landing_page_events')
                   ->selectRaw('campaign_tracking_id, day')
                   ->selectRaw('avg(timestampdiff(second, page_load, page_close)) as time_on_site')
                   ->selectRaw('sum(page_views) as impressions')
                   ->selectRaw('count(campaign_tracking_id) as visits')
                   ->leftJoin('landing_page_offer_events',
                       'landing_page_events.id',
                       '=',
                       'landing_page_offer_events.landing_page_event_id'
                   )
                   ->selectRaw('count(landing_page_offer_events.id) as clicks')
                   ->selectRaw('sum(landing_page_offer_events.conversions) as conversions')
                   ->selectRaw('sum(landing_page_offer_events.revenue) as revenue')
                   ->where('property_id', $this->lander->property_id)
                   ->whereDate('day', now()->toDateString())
                   ->whereNotNull('visitor_processed_at')
                   ->whereNull('bot_detected_at')
                   ->groupBy('campaign_tracking_id', 'day');

Which outputs:

Illuminate\Support\Collection^ {#2163
  #items: array:3 [
    0 => {#2151
      +"campaign_tracking_id": "28ec6fbb-8707-4def-b1c4-a154b23bafa1"
      +"day": "2020-08-12"
      +"time_on_site": "111202.7273"
      +"impressions": "57"
      +"visits": 11
      +"clicks": 4
      +"conversions": "1"
      +"revenue": "230"
    }
    1 => {#2162
      +"campaign_tracking_id": "7d232f7c-05df-4a4a-bfcf-1be4b953574b"
      +"day": "2020-08-12"
      +"time_on_site": "144425.5000"
      +"impressions": "10"
      +"visits": 2
      +"clicks": 0
      +"conversions": null
      +"revenue": null
    }
    2 => {#2169
      +"campaign_tracking_id": "7c2246b7-8b37-4366-90e2-86bb37601343"
      +"day": "2020-08-12"
      +"time_on_site": "275949.0000"
      +"impressions": "9"
      +"visits": 1
      +"clicks": 0
      +"conversions": null
      +"revenue": null
    }
  ]
}

Now I have about 9 other dimensions, things like country, referrer, device etc. For all these the query would be almost the same as the one above, just with a few more columns in the select and also in the groupBy method.

This for example would be the query for the country dimension:

$country_overview = DB::table('landing_page_events')
                   ->selectRaw('country_name, campaign_tracking_id, day')
                   ->selectRaw('avg(timestampdiff(second, page_load, page_close)) as time_on_site')
                   ->selectRaw('sum(page_views) as impressions')
                   ->selectRaw('count(campaign_tracking_id) as visits')
                   ->leftJoin('landing_page_offer_events',
                       'landing_page_events.id',
                       '=',
                       'landing_page_offer_events.landing_page_event_id'
                   )
                   ->selectRaw('count(landing_page_offer_events.id) as clicks')
                   ->selectRaw('sum(landing_page_offer_events.conversions) as conversions')
                   ->selectRaw('sum(landing_page_offer_events.revenue) as revenue')
                   ->where('property_id', $this->lander->property_id)
                   ->whereDate('day', now()->toDateString())
                   ->whereNotNull('visitor_processed_at')
                   ->whereNull('bot_detected_at')
                   ->groupBy('country_name', 'campaign_tracking_id', 'day');

Is it possible to combine all these into a single query, grouped by an alias so that I can hand off each aliased collection to a job for further processing?

I'm really unsure how to go about this, or if my only option are indeed, multiple queries.

Jul
08
3 months ago
Activity icon

Started a new Conversation Eloquent Relationship, Allow Only One Per (single Model) Parent Type.

I have the need to assign an Eloquent model a relationship, where it's only allowed to assign one Model per type. But it's not different Model classes as in a polymorphic, but the same model.

Trying to explain a little better, given we have these Models:

  • Domain
  • Account

Account can be accounts of different sources, indicated by a service column on the model.

Now I need to assign Domain models to Account models, one Domain model can belong to many Account models, but only with unique service column.

I'm sure this isn't unusual, and that I'm simply not seeing the wood because of all the trees in the way... :)

Plz halp! haha

Jun
30
4 months ago
Activity icon

Replied to Undefined Property, But When Dumped It Exists. How Can That Happen?

As so often, shortly after posting I saw the problem haha. It was running a loop and somewhere this was indeed not set!! Sighhhh :)

Activity icon

Started a new Conversation Undefined Property, But When Dumped It Exists. How Can That Happen?

I'm working with an external API, when retrieving a response I get an exception that the property is not defined. But it is, and if I dd it, it also displays... How can that even happen? I'm honestly lost how to even begin debugging this.

 ErrorException

  Undefined property: stdClass::$Campaign

This is the code:

$response = $this->client
            ->call($this->client->getCampaignManagementService(), 'GetCampaignsByAccountId', $request)
            ->Campaigns
            ->Campaign;
dump($response);

As you can see I added a dump there, this is the output: (with some redactions)

array:2 [
  0 => {#1176
    +"AudienceAdsBidAdjustment": null
    +"BiddingScheme": {#1171
      +"Type": "EnhancedCpc"
    }
    +"BudgetType": "DailyBudgetStandard"
    +"DailyBudget": xxx
    +"ExperimentId": null
    +"FinalUrlSuffix": null
    +"ForwardCompatibilityMap": {#2136}
    +"Id": xxx
    +"Name": "xxx"
    +"Status": "Paused"
    +"SubType": null
    +"TimeZone": "EasternTimeUSCanada"
    +"TrackingUrlTemplate": null
    +"UrlCustomParameters": null
    +"CampaignType": "Search"
    +"Settings": null
    +"BudgetId": null
    +"Languages": {#1199
      +"string": array:1 [
        0 => "All"
      ]
    }
  }
  1 => {#1911
    +"AudienceAdsBidAdjustment": null
    +"BiddingScheme": {#2066
      +"Type": "EnhancedCpc"
    }
    +"BudgetType": "DailyBudgetStandard"
    +"DailyBudget": xxx
    +"ExperimentId": null
    +"FinalUrlSuffix": null
    +"ForwardCompatibilityMap": {#2129}
    +"Id": xxx
    +"Name": "xxx"
    +"Status": "Paused"
    +"SubType": null
    +"TimeZone": "EasternTimeUSCanada"
    +"TrackingUrlTemplate": null
    +"UrlCustomParameters": null
    +"CampaignType": "Search"
    +"Settings": null
    +"BudgetId": null
    +"Languages": {#2130
      +"string": array:1 [
        0 => "All"
      ]
    }
  }
]

   ErrorException

  Undefined property: stdClass::$Campaign

  at app/Clients/Platforms/Operations/Bing/Get.php:157
    153|          */
    154|         $response = $this->client
    155|             ->call($this->client->getCampaignManagementService(), 'GetCampaignsByAccountId', $request)
    156|             ->Campaigns
  > 157|             ->Campaign;
    158| dump($response);

So it dumps out the actual response object, just as expected, and then errors out that the property is not defined, which it clearly is though...

This is the call method I'm using:

public function call($service, $method, $args = null)
    {
        try {

            $soapClient = $this->service($service)->GetService();

            return $soapClient->{$method}($args);

        } catch (SoapFault $e) {

            throw new ClientException($e->getMessage(), 0, $e);
        }
    }

Anyone got an idea what is going on here? I have never seen anything like it

Jun
14
4 months ago
Activity icon

Replied to Is This A Case For Repository Pattern?

@martinbean I see, thanks!

Is there a pattern/type of class used for jobs like the above?

Activity icon

Started a new Conversation Is This A Case For Repository Pattern?

I have an application that downloads potentially heavy csv files from third-party sources. (10K - 1MM rows) Now because each row in these csv files would need to be imported, a model created along with relationships etc, I decided to first import a file using LOAD DATA LOCAL INFILE, creating a dynamic table and saving this table name along with the model namespace that it should be later updated into.

Because it is possible that there are different reports, with different target models...

protected $reports = [
        TargetModel::class => 'reportType',
    ];

I loop over the above, download file, import and then save it in a table like:

// before here I dynamically get the columns for TargetModel::class
$table = $importer->loadIntoDynamicTable($file, $columns);

$report->table_name = $table;
$report->target_model = $this->import->model;
$report->save();

So now I have a dynamically created table, like tmp_HDkjhhfhddh8588, with data imported into it. Then I have a job that loops over these dynamic tables, the job so far knows only that the data from that table should be updated into TargetModel.

public function handle()
    {
        DB::beginTransaction();

        try {

            DB::table($this->temporary->table_name)->cursor()->each(function ($report) {

                // todo: write some kind of import class that knows how to create relationships on target model
                //$target = (new $this->temporary->target_model);
                //$target->save((array) $report);
            });

            DB::commit();

            $this->dropTable();

        } catch (Exception $e) {

            $this->dropTable();

            // do not re-run this job.
            $this->delete();

            throw $e;
        }

    }

I'm unsure what kind of class I could write that has knowledge of how to update the data and create the relationships.

Now I was reading about the repository pattern, it seems this would be the correct way of handling it? Just calling the repository from inside the above DB transaction.

Am I being on the right track there, or could this be solved in a better/more elegant way?

Jun
07
4 months ago
Activity icon

Replied to Mutator/Cast Set Called Without Accessing It, Or I Don't Understand How They Work!

Ok I solved it, it seems that the setter is indeed called, excessively as they say here: https://github.com/laravel/framework/discussions/31778

and it also didn't help that I checked for $model instead of $value in my MoneyCast.

Happy sunday!

Activity icon

Started a new Conversation Mutator/Cast Set Called Without Accessing It, Or I Don't Understand How They Work!

Something is happening in my code that makes absolute no sense to me right now. Been trying to pinpoint the issue now for over two hours but it proves to be very hard to spot, for me.

I am casting money amounts to int when they're saved, and get them as Money value objects. I have been trying this with a custom cast, as well as mutators/accessors, the issue remains the same.

At some point the Money object is cast to a string, which causes it's toString() method to format it like USD 2.0 for example. Then the money library cannot parse that anymore and throws an exception.

But the thing is, I have no idea how that happens because I don't access it directly.

Here is all of the code involved:

// my test method
// Arrange
        $report = factory(AdGroupCriterionPerformanceReport::class)
            ->state('withRelations')
            ->create();

        dump('factories done');

        // Act
        $report->optimize();

the optimize method on my model:

public function optimize(): bool
    {
        return (new OptimizeAdGroupCriterion($this))->execute();
    }

The class that's called by the optimize method:

public function __construct(AdGroupCriterionPerformanceReport $report)
    {
        $this->report = $report;
        $this->spend = $report->spend;
        dump('spend was accessed');

        $this->something = $report->campaign->max_bid; // when commented out, the issue disappears
        dd($this->spend);

    }

Here is the money cast class:

/**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  int  $value
     * @param  array  $attributes
     *
     * @return Money
     */
    public function get($model, $key, $value, $attributes)
    {

        if ($key == 'spend') {
            dump('get spend, type: ' . gettype($value));
        }
        dump($model->getTable() . ' - ' . $key . ': ' . $value);
        return Money::ofMinor($value, $this->loadCurrency());
    }


    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  Money|float  $value
     * @param  array  $attributes
     *
     * @return int
     */
    public function set($model, $key, $value, $attributes)
    {

        if ($key == 'spend') {
            dump('set spend, type: ' . gettype($value));
        }
        dump($model->getTable() . ' - ' . $key . ': ' . $value);
        if ($model instanceof Money) {
            return $model->getMinorAmount()->toInt();
        }

        return Money::of($value, $this->loadCurrency())->getMinorAmount()->toInt();
    }

Here is the output of the dumps in the code above:

PHPUnit 8.5.5 by Sebastian Bergmann and contributors.

"adv_campaigns - max_bid: 65"
"adv_campaigns - min_bid: 6"
"adv_campaigns - max_spend_without_revenue: 495"
"adv_ad_group_criteria - bid: 703"
"set spend, type: integer"
"adv_ad_group_criterion_reports - spend: 77"
"adv_ad_group_criterion_reports - revenue: 210"
"adv_ad_group_criterion_reports - current_cpc: 3228"
"factories done"
"get spend, type: integer"
"adv_ad_group_criterion_reports - spend: 7700"
"spend was accessed"
"set spend, type: object"
"adv_ad_group_criterion_reports - spend: USD 77.00"

Brick\Math\Exception\NumberFormatException : The given value "USD 77.00" does not represent a valid number.

When I comment out this line:

$this->something = $report->campaign->max_bid;

the error doesn't happen, and the dump log's last entry is "spend was accessed".

I absolutely do not understand what is happening there tbh, it shouldn't call the set method at all. Can someone explain to me what my mistake is?

Thanks! :)

Edit To make it a bit clearer, whenever I assign the $report variable or properties of it to something, after I set $this->spend = $report->spend; the Money error pops up because the object was cast to a string.

Jun
06
4 months ago
Activity icon

Replied to Yii Framework - Access Denied For MYSQL

I see your password is empty, try setting MYSQL_ALLOW_EMPTY_PASSWORD in your db config (where ever that is in Yii :) )

Activity icon

Replied to Lookup Table Int Size Defeats Purpose Of Table

You didn't mention anything about a couple hundred million rows :)

Either way, the bigint shouldn't be a big deal. Also:

Corresponding columns in the foreign key and the referenced key must have similar data types. The size and sign of integer types must be the same. The length of string types need not be the same. For nonbinary (character) string columns, the character set and collation must be the same.

https://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html

Activity icon

Replied to Lookup Table Int Size Defeats Purpose Of Table

Honestly sounds like premature and over-optimisation...

If the tables have very little data in them, just use the default bigInt. Not worth the hassle imho as I really don't see how it would improve anything at all :)

Activity icon

Replied to How To Upload Bulk Questions From Excel Sheet To Database In Laravel

fastest is just to use load data local in file:

https://stackoverflow.com/questions/14127529/mysql-import-data-from-csv-using-load-data-infile

https://dev.mysql.com/doc/refman/5.7/en/load-data.html

You can manually type it in your db management software sql console, or also automate it programmatically ofc

Activity icon

Replied to Laravel Testing Third Party API Responses And Socialite

on my phone atm, so can't type a lot of code. But i just did a similar thing and this link was what helped me the most:

https://stefanzweifel.io/posts/how-i-write-integration-tests-for-laravel-socialite-powered-apps

Activity icon

Replied to How To Create Foreign Key Items From External API And Save Into Local DB

on my phone atm but i'll try my best:

Assuming you have a belongsTo relationship set up for Department on the Employee model, you vould just do:

$employee->department()->updateOrCreate([
 'department_code' => clientdata['DepartmentCode']
], [
 'fields_to_update' => 'here',
...
]);

Or if you don't have a relationship, you can just do it in the same loop you have already, using the model facade directly:


Department::updateOrCreate([
 'department_code' => clientdata['DepartmentCode']
], [
 'fields_to_update' => 'here',
...
]);

First array in updateOrCreate is the values it checks for existence, if found it tries to update the values in second array.

If not found it will create a new row using data from both arrays

Activity icon

Replied to How To Create Foreign Key Items From External API And Save Into Local DB

use updateOrCreate() method instead.

https://laravel.com/docs/7.x/eloquent

Then just loop over the results like you do above, and it will update whatever has changed, or create if it doesn't exist.

Activity icon

Replied to Problem With Where To Put Currency Code, Relationships Problems

For now I solved it by adding a default to the Account.

protected $attributes = [
        'currency' => 'USD',
    ];

and then call all account() relationships on other models with withDefault().

But still interested if there's a better way of doing this, as it kind of feels wonky :)

Activity icon

Replied to Problem With Where To Put Currency Code, Relationships Problems

Thanks a lot @martinbean, that makes a lot of sense, the issue is only that the currency code is used by all models under an Account, so saving the code in all tables seems hard to manage?

And you're right, the issue is that the relationship needs to exist first, but since I need to use the currency code in the get/set of the cast so that money is correctly converted to/from minor (int) amounts, the set is always called so that I think, I can't really set the relationship first eh?

But now thinking of saving the currency code directly in all models, I'm unsure, mhhh.

Because all monetary fields use the minor int amount and currency code to calculate between minor amount and total amount, but the Account model is the one that is tied to a currency code. (I'm mirroring the structure of a third-party, so I don't have influence over what model to relate the currency to)

So with this info, do you still think saving the codes directly on all tables make sense? Or can you think of a way how to solve this with relationships?

Activity icon

Started a new Conversation Problem With Where To Put Currency Code, Relationships Problems

I'm really banging my head a bit on how to add currency codes to my multi currency application in a way that the relationships are always set.

Right now my problem is that no matter how much I think about it and then try something else, at some point I run into the same issue: Models can not be saved because relationship to the model holding the currency is not found/null.

I have an Account model, which holds the currency code. For example my factory looks like this:

$factory->define(Account::class, function (Faker $faker) {
    return [
        'remote_account_id' => $faker->shuffleString('ABCDEFGHIJKLMNOPQRSTUVWXYZ-_'),
        'name' => $faker->userName,
        'email' => $faker->companyEmail,
        'currency' => $faker->currencyCode,
        'tracking_id' => $faker->uuid,
        'status' => $faker->randomElement([
            AccountStatuses::Active, AccountStatuses::Paused, AccountStatuses::Draft,
        ]),
    ];
});

$factory->define(AdGroupCriterionPerformanceReport::class, function (Faker $faker) {

    return [
        'day' => dateRange(Carbon::now()->subDays(30), Carbon::now()),
        'conversions' => $faker->numberBetween(0, 250),
        'conversion_rate' => $faker->randomFloat(2, 1, 100),
        'spend' => $faker->numberBetween(0, 300),
        'revenue' => $faker->numberBetween(0, 300),
        'impressions' => $faker->numberBetween(0, 30000),
        'clicks' => $faker->numberBetween(0, 3000),
        'quality_score' => $faker->numberBetween(0, 10),
        'click_through_rate' => $faker->randomFloat(2, 0, 100),
        'current_cpc' => $faker->numberBetween(0, 6000),
        'average_position' => $faker->numberBetween(0, 100),
    ];
});

$factory->state(AdGroupCriterionPerformanceReport::class, 'withRelations', function (Faker $faker) {
    return [
        'adv_account_id' => factory(Account::class)->lazy(),
        'adv_campaign_id' => factory(Campaign::class)->lazy(),
        'adv_ad_group_id' => factory(AdGroup::class)->lazy(),
        'adv_ad_group_criterion_id' => factory(AdGroupCriterion::class)->lazy(),
    ];
});

When I now try to write a test for example, it errors out because relationship doesn't exist.

// Arrange
        $account = factory(Account::class)->create();
        $report = factory(AdGroupCriterionPerformanceReport::class)
            ->state('withRelations')
            ->create(['adv_account_id' => $account->id]);
ErrorException : Trying to get property 'currency' of non-object
 /opt/project/app/Casts/MoneyCast.php:41

And I have a cast class on the models that have money fields:

<?php


namespace App\Casts;


use Brick\Money\Money;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;

class MoneyCast implements CastsAttributes
{

    /**
     * Cast the given value.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  string  $value
     * @param  array  $attributes
     *
     * @return Money
     */
    public function get($model, $key, $value, $attributes)
    {
        return Money::ofMinor($value, $model->account->currency);
    }


    /**
     * Prepare the given value for storage.
     *
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @param  string  $key
     * @param  string  $value
     * @param  array  $attributes
     *
     * @return string
     */
    public function set($model, $key, $value, $attributes)
    {
        return Money::of($value, $model->account->currency)->getMinorAmount()->toInt();
    }

}

I'm unsure now, is the way I structured this, wrong? Should a currency code have a direct relationship on all models that need it? Or is accessing it through a distant relationship in general okay?

Is there a better way to handle this?

Jun
05
4 months ago
Activity icon

Replied to Access $amount In Factory Possible?

Ok thanks for that, I guess :) I think I know now what you meant! Thanks!

Activity icon

Replied to Access $amount In Factory Possible?

Thanks! But, are you saying when using 'times()' it is possible to get that dynamically inside the closure? Or did you mean i'd have to set a variable inside the definition myself? (which I am doing atm)

Activity icon

Replied to Access $amount In Factory Possible?

lol :) Thanks, but that was just my pseudo code example, sadly that doesn't work

Activity icon

Started a new Conversation Access $amount In Factory Possible?

Is it somehow possible to get the $amount specified in the factory(SomeClass::class, 30) helper, from inside the factory closure?

I need to reset a helper method every xy iterations, and would love if I can simply access that number somehow.... :)

ie:

$factory->define(......, function(Faker $faker) {
 $amount = $factory->amount; // 30
}
Jun
04
4 months ago
Activity icon

Replied to 1292 Incorrect Datetime Value With Protected $dates & Faker

Sorry, I meant I have a custom date field...

When i tinker it, I get a proper date string:

>>> $faker->dateTimeBetween('-6 months', 'now')->format('Y-m-d')
=> "2019-12-23"
>>> 

But whenever I try to seed I get this error.

migration:

$table->date('date');

model:

/**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = [
        'date',
    ];
Activity icon

Started a new Conversation 1292 Incorrect Datetime Value With Protected $dates & Faker

I'm writing seeders, and I have one model with $timestamps = false. On that model I have a custom timestamp field, and I cast it using the $dates = [] property.

Now in my seeder I have:

'date' => $faker->dateTimeBetween('-6 months', 'now')->format('Y-m-d'),

But I always get an error:

Invalid datetime format: 1292 Incorrect datetime value: '2020-03-07T08:59:07.000000Z' for column 'date' at row 1

Tried a shitload of things, but nothing works, only when I comment out the

protected $dates = [
        'date',
    ];

the timestamp field saves...

What am I doing wrong? Laravel's documentation says that $dates can be passed in various formats, such as a simple date string like 2019-12-23, but it does not work.

Jun
03
4 months ago
Activity icon

Replied to $visitor = Tracker::currentSession(); Return Null Data.

Are you sure you've recorded visits already, do any show up in your database?

Activity icon

Replied to Seeding Only A Few Models & Relationships Takes Close To 2 Min, How To Improve>

Thanks!

I've played around a bit, and moved all the relationship creation into the factories, like:

return [
	'some_id' => factory(SomeClass::class),
];

And now my seeder only has one factory call, for the class that is the lowest in the relationship hierarchy. It is much faster now.

Is this a good practice to get into for seeders?

Seed a class that is the lowest in the hierarchy, have factories create all their relationships, so when you call the lowest hierarchy factory all the relationship created kinda "bubble up" from that point?

Activity icon

Started a new Conversation Seeding Only A Few Models & Relationships Takes Close To 2 Min, How To Improve>

I just started testing and seeding in general, and I'm using the PhpStorm integration in combination with Vessel for Docker.

I have a single seeder atm. which creates only a few models and relationships, but already takes close to 2 minutes to complete.

Is there anything I can do to improve on this, or is it what it is and testing/seeding is always slow?

/**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {

        factory(Platform::class, 2)->create()->each(function ($platform) {

            $customers = $platform->platformCustomers()->saveMany(
                factory(PlatformCustomer::class, 2)->make()
            );

            $customers->each(function ($customer) {
                $accounts = $customer->platformAccounts()->saveMany(
                    factory(PlatformAccount::class, 3)->make()
                );

                $accounts->each(function ($account) {
                    $account->platformCampaigns()->saveMany(
                        factory(PlatformCampaign::class, 3)->make()
                    );
                });
            });

        });

        $campaigns = PlatformCampaign::all();
        $campaigns->each(function ($campaign) {
            $adGroups = $campaign->platformAdGroups()->saveMany(
                factory(PlatformAdGroup::class, 5)->make()
            );

            $adGroups->each(function ($adGroup) {
                $adGroupCriteria = $adGroup->platformAdGroupCriteria()->saveMany(
                    factory(PlatformAdGroupCriterion::class, 5)->make()
                );

                $adGroupCriteria->each(function ($adGroupCriterion) {
                    $adGroupCriterion->platformAdGroupCriterionPerformanceReports()->saveMany(
                        factory(PlatformAdGroupCriterionPerformanceReport::class, 10)->make()
                    );
                });
            });
        });
    }
Jun
01
4 months ago
Activity icon

Started a new Conversation When Testing, And Your App Requires Third-party API Data, How/what To Test First?

When testing, and your app requires third-party API data, how/what to test first?

So I'm finally starting to write tests :) First project where I decide to do this, will get all of it's data (except local users) from third-party apis.

How would you start testing, what tests to write first? Actually having the tests connect via API, get results and then do something with it? Fake all of the data, and once at that point, only test if a connection is successful i.e returns something? What if certain data cannot be faked?

Maybe you guys can give me some good pointers here, I would certainly appreciate any :)

May
30
5 months ago
Activity icon

Replied to Confusion About Naming & Relationships For Models With Same Name But Different Structure/use Case

Thanks @bobbybouwmann, you've hit the nail on the head. What you said makes a lot of sense!

Gives me actually confidence in my latest decision to prefix all models with their domain, ie:

App\Models\Platforms:

  • Platform
  • PlatformCustomer
  • PlatformAccount
  • Platform...

App\Models\Networks:

  • Network
  • NetworkAccount
  • Network...

Then instead of following Laravel's convention for foreign keys, I will leave out the prefix there and set the foreign key names inside each relationship method.

This will result in long model names, but ultimately it might also be nice for auto-suggest in PhpStorm etc. You always get everything related to one domain immediately when typing plat...

Activity icon

Started a new Conversation Confusion About Naming & Relationships For Models With Same Name But Different Structure/use Case

Hello fellow Laracaster's :)

I'm banging my head a little on this one, and hope you can give me some pointers.

Trying to come up with names and proper relationships for models that will have a different use case in my app, but have the same names.

There are two distinctions: Platforms & Networks Each represent a different use case in my app, and consist of multiple child models.

What I have:

Platforms:

  • Platform
  • Customer
  • Account

Networks:

  • Network
  • Account

Now the Account model for each of these use cases, will share just a few column names like name and email, the rest will be completely different, even though they practically represent the same thing (A user/customer owning an account)...

For example the Account model for a Platform will have a column tracking_uuid, and is involved in tracking visitors on websites, while the Network Account model won't have this column and will simply be used to label statistics.

The more I plan relationships now, the more I realize that down the line there will be a lot of confusion with method names, table names and so forth.

Now I've tried multiple things, from moving them into their own namespaces like App\Models\Platforms\Account to trying to prefix all models with either Platform or Network, but in the end the column names of database migrations get simply too long, it becomes messy and ultimately a little confusing.

Now I was thinking if using polymorphic relationships might be the solution, but I'm unsure where I would then move the columns to that are different in each model, and if doing that might get me into trouble down the line somewhere.

Any pointers/ideas how I could improve this?

PS: might be a good idea to also mention the relationships... :)

  • Platform can have many Customer who can own many Account.
  • Network can have many Account

For a Network, the Account model is basically the user/customer, while a Platform has Customers which own Accounts

May
28
5 months ago
Activity icon

Commented on Simpler Code With UseCase Classes

Great explanation @jeffreyway :)

When you created the directory UseCases, I was thinking Features would be a better description, as in Feature tests. Because that is basically what it is, one of the main features of the application.

Or do you think that would be confusing, as features is also terminology that has other meanings in a programming context.

I.e: When naming things, do you try to avoid names that already have other meanings in the Framework, even if you think they would fit better?