pilat

Experience

11,260

0 Best Reply Awards

  • Member Since 1 Year Ago
  • 95 Lessons Completed
  • 0 Favorites

1st August, 2018

pilat left a reply on Single-file Components: Failed To Mount Component: Template Or Render Function Not Defined. • 2 weeks ago

A workaround ("kostillie"): replace all .js with .react.

mix.react('resources/assets/js/app.js', 'public/js')
   .react('resources/assets/js/map.js', 'public/js')
   .react('resources/assets/js/calendar.js', 'public/js');
   // … few more deps

pilat left a reply on Troubleshooting "Serialization Of Closure Is Not Allowed" Issue When Caching Routes • 2 weeks ago

I've found a duplicate route that fixed this issue.

The error message is very misleading, I'd say…

pilat started a new conversation Single-file Components: Failed To Mount Component: Template Or Render Function Not Defined. • 2 weeks ago

Hi,

I've trying to migrate from elixir to mix in my Laravel 5.3 project. What I've done so far:

  1. Rewritten package.json to be "like in laravel 5.6":
  "devDependencies": {
    "axios": "^0.18",
    "babel-preset-react": "^6.24.1",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^5.1",
    "gentelella": "^1.4.0",
    "laravel-echo": "^1.4.0",
    "laravel-mix": "^2.0",
    "laravel-vue-pagination": "^1.2.0",
    "portal-vue": "^1.3.0",
    "pusher-js": "^4.2.2",
    "vue": "^2.5.7",
    "vuedraggable": "^2.16.0"
  },

Note: I'm loading jQuery and some other "non-vue" libs from CDN, so, they're not in this list.

  1. Clean-reinstalled all dependencies: rm -rf node_modules package-lock.json && npm cache clear --force && npm install

  2. In resources/js/bootstrap.js, replaced vue-resource with axios:

window.axios = require('axios');

window.axios.defaults.headers.common = {
    'X-CSRF-TOKEN': window.Laravel.csrfToken,
    'X-Requested-With': 'XMLHttpRequest'
};

Vue.prototype.$http = axios;

  1. Added function mix(...) implementation to my app/helpers.php

  2. "converted" gulfile.js to webpack.mix.js:

let mix = require('laravel-mix');
mix.sass('resources/assets/sass/app.scss', 'public/css')
   .sass('resources/assets/sass/accounts_custom.scss', 'public/css')
   .sass('resources/assets/sass/map.scss', 'public/css')
   .sass('resources/assets/sass/calendar.scss', 'public/css');

mix.js('resources/assets/js/app.js', 'public/js')
   .js('resources/assets/js/map.js', 'public/js')
   .react('resources/assets/js/calendar.js', 'public/js');
   // … few more deps

if (mix.inProduction()) {
    mix.version();
}
  1. Built the project: npm run dev

Now, in my browser, I get errors like this in every place, where any component from .vue file is referenced:

map.js:1449 [Vue warn]: Failed to mount component: template or render function not defined.

found in

---> <Waiter> at resources/assets/js/map_components/Waiter.vue
       <Root>

, here's how this specific component is plugged in:

Vue.component('waiter', require('./map_components/Waiter.vue'));

But, the problem is the same with any component…

What am I missing?

26th July, 2018

pilat left a reply on Casting To Boolean • 3 weeks ago

I'm afraid it's confusing and may lead to errors. Here's the documentation:

Now the is_admin attribute will always be cast to a boolean when you access it, even if the underlying value is stored in the database as an integer:

Ok, so I can check it like this: $user->is_admin === true?

nope.

# tinker

>>> App\Activity::first()->processed === 0
=> true

>>> App\Activity::first()->processed === false
=> false

25th July, 2018

pilat started a new conversation Troubleshooting "Serialization Of Closure Is Not Allowed" Issue When Caching Routes • 3 weeks ago

I'm trying to cache routes in Laravel 5.3-based project. Here's the error I get:

$ php artisan route:cache -v

Route cache cleared!


  [Exception]
  Serialization of 'Closure' is not allowed


Exception trace:
 () at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteCacheCommand.php:95
 serialize() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteCacheCommand.php:95
 Illuminate\Foundation\Console\RouteCacheCommand->buildRouteCacheFile() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Foundation/Console/RouteCacheCommand.php:65
 Illuminate\Foundation\Console\RouteCacheCommand->fire() at n/a:n/a
 call_user_func_array() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Container/Container.php:508
 Illuminate\Container\Container->call() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Console/Command.php:169
 Illuminate\Console\Command->execute() at /Users/pilat/version-control/mappanel/vendor/symfony/console/Command/Command.php:261
 Symfony\Component\Console\Command\Command->run() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Console/Command.php:155
 Illuminate\Console\Command->run() at /Users/pilat/version-control/mappanel/vendor/symfony/console/Application.php:817
 Symfony\Component\Console\Application->doRunCommand() at /Users/pilat/version-control/mappanel/vendor/symfony/console/Application.php:185
 Symfony\Component\Console\Application->doRun() at /Users/pilat/version-control/mappanel/vendor/symfony/console/Application.php:116
 Symfony\Component\Console\Application->run() at /Users/pilat/version-control/mappanel/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php:121
 Illuminate\Foundation\Console\Kernel->handle() at /Users/pilat/version-control/mappanel/artisan:35

Important notes:

  • I've already moved all Closures from route/*.php files to controllers and re-checked it several times. The only closures in these files remaining are in Route::group constructions.
  • I've tried a script like this, jut to be on the safe side (it returned nothing):
# tinker

$routes = app('router')->getRoutes()

foreach ($routes as $route) {
    if (is_object($route) && ($route instanceof Closure)) {
        $r = new ReflectionFunction($route);
        dump($r->getFilename());
    }
}

10th July, 2018

pilat left a reply on Show Uncaught Exceptions To Console Rather Than Laravel.log File • 1 month ago

here's for 5.4: https://gist.github.com/adamwathan/125847c7e3f16b88fa33a9f8b42333da

But it worked for 5.3 as well (copied only the interesting pieces over, not the whole php file).

5th July, 2018

pilat left a reply on Valet, Custom Driver With Other PHP Version • 1 month ago

@willvincent I like your idea of switching between installed phps, but it doesn't work for some reason…

Here's my aliases:

# PHP versions
alias use56="brew unlink [email protected] && brew unlink php && brew link --force [email protected] && valet restart"
alias use70="brew unlink [email protected] && brew unlink php && brew link --force [email protected] && valet restart"
alias use72="brew unlink [email protected] && brew unlink [email protected] && brew link php && valet restart"

And here's the result of using one of them:

✗ use56
Unlinking /usr/local/Cellar/[email protected]/7.0.30_1... 0 symlinks removed
Unlinking /usr/local/Cellar/php/7.2.7... 0 symlinks removed
Warning: [email protected] is keg-only and must be linked with --force
Note that doing so can interfere with building software.

If you need to have this software first in your PATH instead consider running:
  echo 'export PATH="/usr/local/opt/[email protected]/bin:$PATH"' >> ~/.zshrc
  echo 'export PATH="/usr/local/opt/[email protected]/sbin:$PATH"' >> ~/.zshrc

In Brew.php line 182:

  Unable to determine linked PHP.


restart

Could you help me to get this working?

30th June, 2018

pilat left a reply on How Do I Get List Of All Available Attributes In The Model? • 1 month ago

It looks I can do i this way:

return array_key_exists($key, $this->attributes) || $this->hasGetMutator($key));

Let me test it out :-)

pilat started a new conversation How Do I Get List Of All Available Attributes In The Model? • 1 month ago

Hi,

What I need is to get a list of all available attributes (keys) on the model, including accessors.

Here's what I'm trying to do:

public function fillData($template)
{
    preg_match_all('/{([^}]+)}/', $template, $matches);
    $placeholders = $matches[1];

    $attributes = $this->getFullAttributesList(); // <<< HOW??

    $replaceData = [];

    foreach ($placeholders as $placeholder) {
    if (in_array($placehoder, $attribute)) {
        $replaceData[$placeholder] = $this->{$attribute};
        }
    }

    return $this->miniTwig($template, $replaceData);
}

protected function miniTwig($template, $data)
{
    $what = array_map(function ($key) {
        return '{' . $key . '}';
    }, array_keys($data));

    $with = array_values($data);

    return str_replace($what, $with, $template);
}


protected function getFullAttributesList()
{
    // is there a way?
}


/// Useage exmaple:

$model->fillData("The product {product_name} costs {price");

9th April, 2018

pilat started a new conversation How To Receive And Process Request Known To Be In Non UTF-8 Charset? • 4 months ago

Hi,

Here's the issue: one of sites sends data to my app in a non-UTF-8 charset. That site is not under my control, so, let I it be as it is.

Now, when I'm trying to process this data, I have few issues.

  1. I cannot log the request data.
Log::debug("[[email protected]{$site}] request", [ $request->all() ]);

Log::debug("[[email protected]{$site}] request", [ $_REQUEST ]);

Both lines produce the following in the log file:

[2018-04-09 15:40:05] production.DEBUG: [[email protected]] request

(there's not even "[]" after the "message" part and it's strange…)

  1. Later on, when I try to dispatch a Job with this data as payload, I get the following error:

[2018-04-09 15:40:06] production.ERROR: exception 'InvalidArgumentException' with message 'Unable to create payload: Malformed UTF-8 characters, possibly incorrectly encoded' in /home/i/ivento/kartazamerov.ru/public_html/mappanel/vendor/laravel/framework/src/Illuminate/Queue/Queue.php:94

  1. Error stays the same even if I try to covert this data from the original charset to UTF-8:
        $payload = $request->all();
        if ('ThatDinozaurus' === $SiteName) {
            $payload = iconv('CP1251', 'UTF-8', json_encode($payload));
            $payload = json_decode($payload, true);
        }

The task: to be able to handle those requests as well as other, good'ol utf-8 ones. I know which site sends non-standard charset. I have iconv at my disposal. But still something's wrong…

27th February, 2018

pilat left a reply on Can I Cancel Event Broadcasting From Within The Event Class? • 5 months ago

Okey, I'll stick with helpers, again =)

  1. Make a helper function. E.g., this one:
function pusher($event) // @tido find better name with lower risk of collision
{
    if (is_null($event->broadcastOn())) {
        return;
    }

    event($event);
}
  1. In the Event class, you can make all the checks in ::broadcastOn() and return null if it shouldn't be published:
class SomethingWasSynced implements ShouldBroadcast
{

    // ...

    public function broadcastOn()
    {
        if (! $this->broadcastWhen()) {
            return null;
        }

        return new Channel("syncing-{$this->subdomain}");
    }

    public function broadcastWhen() // if I ever upgrade to 5.6+, I'll leave this method as is, but remove that check from `::broadcastOn()` ;-)
    {
        if (App::runningInConsole())
            return false;
        }

        return true;
    }
}
  1. Use pusher() instead of event() in your client code:
pusher(new SomethingWasSynced($this->subdomain, 'custom_fields', $count));

26th February, 2018

pilat left a reply on Can I Cancel Event Broadcasting From Within The Event Class? • 5 months ago

@mrbadr doesn't work.

I've tried:

  1. Returning null from __construct();
  2. Returning null from broadcastOn();

Having BROADCAST_DRIVER=log in the .env file, I can see that broadcasting is still happening:

[2018-02-26 17:56:37] local.INFO: Broadcasting [App\Events\SomethingWasSynced] on channels [] with payload:
{
    "broadcastQueue": "pusher",
    "subdomain": "domain1",
    "what": "activities",
    "totalCount": 40,
    "socket": null
}

Just the list of channels is empty. I am wondering: will these messages (with the empty list of channels) be actually sent to Pusher and eat up my notifications limits?

pilat left a reply on Can I Cancel Event Broadcasting From Within The Event Class? • 5 months ago

The second question is: how do I tell a web request (Http\Kernel) from a Console one (Console\Kernel)? Can I do it from within the event class as well?

It seems I can use this guy: App::runningInConsole()

pilat left a reply on Can I Cancel Event Broadcasting From Within The Event Class? • 5 months ago

UPDATE: I've just found broadcastWhen() method in the documentation for Laravel 5.6, but there is no such method in Laravel 5.3. So, need to find the other way around.

pilat started a new conversation Can I Cancel Event Broadcasting From Within The Event Class? • 5 months ago

Hi, I have an event class to send notifications about external API sync status. It's quite simple, and I'm appending the code to the end of this post.

Now, I have one task: I'd only like to broadcast this event if the following conditions are both met:

  1. It's not Local environment;
  2. t is Http kernel, not Console (because the syncing can also be initiated as Artisan command or a scheduled task and there is no use for websockets in this case).

Now, I'm using something like this in my "client code" when firing the event:

        if (! App::environment('local')) {
            event(new SomethingWasSynced($this->subdomain, 'custom_fields', $this->cfsGeneratedCount));
        }

The problem: I have to remember to use this conditions every time I need to fire this event. Besides, it's not very dry this way.

So the question is: can I check this condition and cancel/abort broadcasting from within the event itself? For example, by returning false in constructor or calling some magic method, I don't know…

The second question is: how do I tell a web request (Http\Kernel) from a Console one (Console\Kernel)? Can I do it from within the event class as well?

class SomethingWasSynced implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    /**
     * The name of the queue on which to place the event.
     *
     * @var string
     */
    public $broadcastQueue = 'pusher';

    public $subdomain;
    public $what;
    public $totalCount;

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

    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new Channel("syncing-{$this->subdomain}");
    }

    public function onQueue()
    {
        return 'pusher';
    }
}

17th January, 2018

pilat left a reply on Search In JSON Column Does Not Work With Query Builder And Eloquent Model (but Works With Raw Query) • 7 months ago

I've found the issue. Hope, it may help someone:

It turns out that linked_company_id is stored as string in the json structure I search in. Like: …,"linked_company_id":"68743242",…n't ask me why, I get this data from external service's API). So, I've tried the following code:

$allLeadsEloquent = LeadsCache::where('json->linked_company_id', (string)$companyId)
    ->get();

And it worked.

Still, I find it not very neat, for at least the following reasons:

  1. How come the Debugbar still produces the query that actually finds rows?

  2. This "strict type match" is quite hard to debug. Especially, considering that type match is not required when searching in normal (not JSON) column.

pilat started a new conversation Search In JSON Column Does Not Work With Query Builder And Eloquent Model (but Works With Raw Query) • 7 months ago

Hi, in Laravel 5.3, I'm trying to use that short syntax for the JSON columns:

$allLeadsEloquent = LeadsCache::where('json->linked_company_id', $companyId)
    ->get();

The result is: empty collection. Note: the same result is with Query Builder (DB('table_name')).

Now. I open Debugbar and grab the "Raw query" produced by this code and run this query in the Sequel Pro: there it finds some rows!

Here's the how the raw query looks:

select * from `leads_cache` where `json`->'$."linked_company_id"' = '68743242'

What possibly could go wrong with this?…

11th January, 2018

pilat left a reply on Performant Way To Mass "update Or Create" • 7 months ago

@kfirba

Finally implemented this approach and had some time to test it out. Looks good so far! At least, I can be sure that relations records IDs stay unchanged (once created) and they're not going to overflow INTEGER type eventually :-)

Thank you very much!

pilat left a reply on How To Make Sure/check If A Listener Is Not Queued? • 7 months ago

There probably was some other error in the code, resulting in that data not to become immediately available… Don't remember how I've fixed it, to be honest, but the issue is not there anymore.

pilat left a reply on Disabling Vue Devtools In Production • 7 months ago

I'm not sure if I want to create Webpack config specifically for this one option (I have Laravel 5.3). Will these options work "runtime"? I mean, if I do the following in my app.js file:

if (location.hostname.indexOf('production-domain.com') === -1) {
    Vue.config.debug = true;
} else {
    Vue.config.devtools = false;
    Vue.config.debug = false;
    Vue.config.silent = true;
}

10th January, 2018

pilat started a new conversation Setting Wait_timeout For MySQL Connection • 7 months ago

Hi, I need to set wait_timeout session variable for the database connection. BUt:

  1. It has to be done inside my application because the hosting team refuses to change this option "for all of the users". They say I can do it easily by issuing the following query: SET SESSION wait_timeout = XXXXXX.

  2. I need to find a proper place in which to issue this query. As I understand, it should be issued somewhere near connection establishing, is it right? There is no such option in config/database.php and I shouldn't touch files inside vendor/, off cause. Besides, this adjustment should be indexed by git.

So, could you advise a proper way to inject this?

13th December, 2017

pilat left a reply on Is There A Nice Way To Handle Empty Dates In Templates? • 8 months ago

One more question: is there a way to extend Carbon\Carbon in a way, that all Eloquent models would use my custom class for the dates? Meanwhile, I shall probably issue a feature request to Carbon themselves…

12th December, 2017

pilat left a reply on Is There A Nice Way To Handle Empty Dates In Templates? • 8 months ago

There's no evaluation going on except for the PHP interpreter.

I don't now… I'm literally parsing $input into arguments.

If that bothers you then you should probably stop using Blade. The whole thing is based on it.

I see. Let us put it this way: I don't trust myself writing this kind of code. I'm ok with built-in directives, written by masters and covered by loads of tests ;-) I'd better use helper function for this one case.

@tykus: optional() is a thing! I should probably backport this helper into my Laravel 5.3. based project. If I need some fallback text, should I use it Like this?

{{ optional($record->some_date_field)->format('d.m.Y') or 'n/a' }}

pilat left a reply on Is There A Nice Way To Handle Empty Dates In Templates? • 8 months ago

@topvillas Blade extension looks like a plan!

But… Here's what I came to:


// AppServiceProvider.php

// ...
       Blade::directive('dateOrText', function ($input) {
            $args = preg_split('/\s*,\s*/', $input);
            $expression = $args[0];
            $defaultText = isset($args[1]) ? $args[1] : "'-'";
            $format = empty($args[2]) ? "'d.m.Y'" : $args[2];

            return "<?php echo date_or_text($expression, $defaultText, $format); ?>";
        });
// ...

// helpers.php

// ...
function date_or_text($expression, $defaultText = '-', $format = 'd.m.Y')
{
    if (is_numeric($expression)) { // timestamp is passed:
        if (0 === $expression) {
            return $defaultText;
        }
        $expression = \Carbon\Carbon::createFromTimestamp($expression);
    }

    // assume that DateTime object is passed:
    return (int)$expression->format('Y') > 1970 ? $expression->format($format) : $defaultText;
}

// template.blade.php

                <td>
                    @dateOrText($task->complete_till_carbon, '-', 'd.m.Y H:i')<br>
                    @dateOrText($task->complete_till, '-', 'd.m.Y H:i')<br>
                    @dateOrText($task->complete_till, '-')<br>
                </td>

… but the question: is `@dateOrText(…argooking so much better than an `{{ date_or_text(…argshat it worth parsing sing ` in the the `Blade::dir's callback? Besides, the whole hole `Blade::direc concept smells like like ` to me…

So, the final version:

// helpers.php

// ...
function date_or_text($expression, $defaultText = '-', $format = 'd.m.Y')
{
    if (is_numeric($expression)) { // timestamp is passed:
        if (0 === $expression) {
            return $defaultText;
        }
        $expression = \Carbon\Carbon::createFromTimestamp($expression);
    }

    // assume that DateTime object is passed:
    return (int)$expression->format('Y') > 1970 ? $expression->format($format) : $defaultText;
}

// template.blade.php

                <td>{{ date_or_text($task->complete_till_carbon, '-', 'd.m.Y H:i') }}</td>

pilat started a new conversation Is There A Nice Way To Handle Empty Dates In Templates? • 8 months ago

Hi,

Let say, I have a nullable Date or DateTime field in my DB. Eloquent exports it to templates as Carbon object, so, I can freely setup formatting:

<td>{{ $record->some_date_field->format('d.m.Y') }}</td>

This is very convenient, and I'd prefer to have this option available.

The problem is when 'm trying to display dates from those records, that have it empty (or null; or zero, if I store timestamps). Here's a piece of real code:

    public function getCompleteTillCarbonAttribute()
    {
        return \Carbon\Carbon::createFromTimestamp($this->complete_till);
    }
<td>{{ $task->complete_till_carbon->format('Y-m-d') }}</td>

When original complete_till is 0, I'll have 1970-01-01 date on the output. To avoid this, I should do one of the following:

  1. Use conditions in blade:
<td>{{ 0 === $task->complete_till ? 'n/a' : $task->complete_till_carbon->format('Y-m-d') }}</td>
  1. Make a function that returns date as string:
    public function getCompleteTillStringAttribute()
    {
    return 0 === $this->complete_till
        ? 'n/a'
        : date('d.m.Y', $this->complete_till); // don't see any use for Carbon in this case
    }

Neither way is very elegant, I wold say. I'd prefer to control the template to be in charge for appearance, but not to have that ugly ternary condition operator…

Something like this: {{ $record->a_date->format('d.m.Y')->ifEmpty('n/a') }}, or this: {{ $record->a_date->format('d.m.Y', 'n/a') }};

Do anyone have good solution to the issue?

27th November, 2017

pilat left a reply on Eager Load Relations Stored In Json Field • 8 months ago

I'm on the same issue too. Here's my example:

# Eloquent model:
class Activity extends Model
{
    // ...
    public function notifications()
    {
        return $this->hasMany(\App\NotifyLog::class, 'report->activity->element_id', 'element_id');
    }
    // ...
}


# Client code:
# 1. No eager loading:
App\Entities\Activity::has('notifications')->take(3)->get()->map(function ($a) { return $a->notifications; });
# ^^^ works like a charm.

# 2. With eager loading:
App\Entities\Activity::with->('notifications')->has('notifications')->take(3)->get()->map(function ($a) { return $a->notifications; });
# ^^^ nope… Here's the exact results:
# "select * from `activities` where exists (select * from `notify_logs` where `notify_logs`.`report`->'$."activity"."element_id"' = `activities`.`element_id`) limit 3"
# []
# 17504.04
# "select * from `notify_logs` where `notify_logs`.`report`->'$."activity"."element_id"' in (?, ?)"
# array:2 [
#   0 => 15062832
#   1 => 13737651
# ]
# 1.27
# => Illuminate\Support\Collection {#1114
#      all: [
#        Illuminate\Database\Eloquent\Collection {#1085
#          all: [],
#        },
#        Illuminate\Database\Eloquent\Collection {#1087
#          all: [],
#        },
#        Illuminate\Database\Eloquent\Collection {#1061
#          all: [],
#        },
#      ],
#    }

pilat left a reply on With() Does Not Work If I Use Where() In Relations • 8 months ago

Ok. What I understand at this moment is that there has to be an option to use ::withColumn() in the eager-loading query builder.

Something like this:

    public function custom_fields()
    {
        return $this->hasMany(\App\CustomField::class, 'element_id', 'element_id')
            ->whereColumn('custom_fields.subdomain', 'activities.subdomain');
            // or simpler: ->whereColumn('subdomain', 'subdomain');
    }

    public function lead()
    {
        return $this->hasOne(\App\AmoLeadsCache::class, 'id', 'element_id')
             ->whereColumn('leads_cache.subdomain', 'activities.subdomain');
    }
    // this doesn't work, off cause…

At the moment, I'll stick to the custom made local scopes, like this one:

    public function scopeWithLead($query, $subdomain)
    {
        return $query
            ->with([ 'lead' => function ($query) use ($subdomain) {
                $query
                    ->where('subdomain', $subdomain);
            }]);
    }

, although I don't find it very DRY having to repeat that $subdomain constraint on both the Activitiy and its relations:

// finally, the client code:
\App\Entities\Activity::subdomain('subdomain1')->withLead('subdomain1')->take(3)->get();
// don't like that repetition of subdomain constraint however… :/

22nd November, 2017

pilat left a reply on With() Does Not Work If I Use Where() In Relations • 8 months ago

@BryceSharp thank you. It looks similar to my current workaround.

However, I loose eager loading this way. So, if I have, say, 500 activities in the list, I'll execute 500 extra SQL queries to fill them all.

You are assuming that the instance of Activity calling fillData() has the scope of the eager loaded lead.

Yes, this is what bothering me. Why is it exactyl there is no that scope when I use ->where() in the relation functions, but the scope is present when I don't use ->where()? Is there a way I could preserve both the extra constraint in relations AND the eager loading?

21st November, 2017

pilat left a reply on With() Does Not Work If I Use Where() In Relations • 8 months ago

Listening to SQL queries:

>>> DB::listen(function ($query) { dump($query->sql); dump($query->bindings); dump($query->time); });
=> null
>>> App\Entities\Activity::with('lead')->latest()->take(3)->get()->map(function ($a) { return $a->fillData('{name}: {url}'); });
"select * from `activities` order by `created_at` desc limit 3"
[]
74.0
"select * from `leads_cache` where `leads_cache`.`subdomain` is null and `leads_cache`.`id` in (?, ?, ?)"
array:3 [
  0 => 4879927
  1 => 5219288
  2 => 5673393
]
13.18
=> Illuminate\Support\Collection {#1034
     all: [
       ": ",
       ": ",
       ": ",
     ],
   }

So, it looks for null subdomain for some reason…

But if there's no eager loading, it makes proper queries (just too many of them):

>>> App\Entities\Activity::latest()->take(3)->get()->map(function ($a) { return $a->fillData('{name}: {url}'); });
"select * from `activities` order by `created_at` desc limit 3"
[]
80.55
"select * from `leads_cache` where `leads_cache`.`id` = ? and `leads_cache`.`id` is not null and `leads_cache`.`subdomain` = ? limit 1"
array:2 [
  0 => 4879927
  1 => "mysubdomain"
]
8.77
"select * from `leads_cache` where `leads_cache`.`id` = ? and `leads_cache`.`id` is not null and `leads_cache`.`subdomain` = ? limit 1"
array:2 [
  0 => 5219288
  1 => "mysubdomain"
]
1.31
"select * from `leads_cache` where `leads_cache`.`id` = ? and `leads_cache`.`id` is not null and `leads_cache`.`subdomain` = ? limit 1"
array:2 [
  0 => 5673393
  1 => "mysubdomain"
]
0.75
=> Illuminate\Support\Collection {#1028
     all: [
       "Name 1: https://url1",
       "Name 2: https://url2",
       "Name 3: https://url3",
     ],
   }

pilat left a reply on With() Does Not Work If I Use Where() In Relations • 8 months ago

Ok, I made that client code up, in fact.

Here's the part of the same Activity class that does not work:

class Activity extends Model
{
    // ...

    public function fillData($template)
    {
        if (strpos($template, '{price}') !== false) {
            $replaceData['price'] = empty($this->lead->price) ? '0' : $this->lead->price;
        }
        if (strpos($template, '{url}') !== false) {
            $replaceData['url'] = empty($this->lead->url) ? '[no url]' : $this->lead->url;
        }
        if (strpos($template, '{name}') !== false) {
            $replaceData['name'] = empty($this->lead->name) ? '[no name]' : $this->lead->name;
    }

    // ...
}

Now goes the client code:

1. No eager loading:

>>> App\Entities\Activity::latest()->take(3)->get()->map(function ($a) { return $a->fillData('{name}: {url}'); });

=> Illuminate\Support\Collection {#1026
     all: [
       "Name 1: https://url1",
       "Name 2: https://url2",
       "Name 3: https://url3",
     ],
   }

2. With eager loading:

>>> App\Entities\Activity::with('lead')->latest()->take(3)->get()->map(function ($a) { return $a->fillData('{name}: {url}'); });

=> Illuminate\Support\Collection {#1028
     all: [
       ": ",
       ": ",
       ": ",
     ],
   }

3. Still with eager loading, but without "::where()" in relation functions (my very first example):

>>> App\Entities\Activity::with('lead')->latest()->take(3)->get()->map(function ($a) { return $a->fillData('{name}: {url}'); });

=> Illuminate\Support\Collection {#1026
     all: [
       "Name 1: https://url1",
       "Name 2: https://url2",
       "Name 3: https://url3",
     ],
   }

20th November, 2017

pilat started a new conversation With() Does Not Work If I Use Where() In Relations • 8 months ago

Hi, I've stumbled over this issue.

Laravel 5.3

Here's couple of examples:

class Activity extends Model
{
    public function custom_fields()
    {
        return $this->hasMany(\App\CustomField::class, 'element_id', 'element_id');
    }

    public function lead()
    {
        return $this->hasOne(\App\AmoLeadsCache::class, 'id', 'element_id');
    }
}

class Activity extends Model
{
    public function custom_fields()
    {
        return $this->hasMany(\App\CustomField::class, 'element_id', 'element_id')
            ->where('custom_fields.subdomain', $this->subdomain);
    }

    public function lead()
    {
        return $this->hasOne(\App\AmoLeadsCache::class, 'id', 'element_id')
            ->where('leads_cache.subdomain', $this->subdomain);
    }
}

Now, in client's code I do the following:

Activity::with('custom_fields', 'lead')->where(/*some other cond*/)->get();

It works as expected if I don't have ->where() in the "relations" methods. If i have, however, all relations are empty (lead=>null, custom_fields=>[]).

Anyone knows how to overcome this? I really need to preserve those ->where() clauses, but I also need eager loading.

18th October, 2017

pilat started a new conversation Arrow Keys Don't Work In Tinker • 10 months ago

Help, please,

There's a problem which causes me lots of pain when I'm trying to use tinker on production server.

In the local development I can use Up and Down keys to browse commands history. Also, I often use Ctrl+{A,E} to move to the line start/end. None of this works when I'm on remote server via ssh. Even Left and Right keys are not working :-(

Here's, what I get when pressing this or that key:

>>> Up arrow: ^[[A ; Down arrow: ^[[B ; Left: ^[[D ; Right: ^[[C ; Ctrl+A: ^A ; Ctrl+E: ^E

Terminal: iTerm2

Remote server OS: some sort of linux... uname -a only says it's Linux and then displays the host name.

Note: I can use arrows in shell (/bin/bash), when connected to that server. The issue is only in tinker.

13th September, 2017

pilat left a reply on Performant Way To Mass "update Or Create" • 11 months ago

@kfirba Thank you, this is something I've thought of, actually, just wanted to avoid slipping off the ORM-way. Also, there is a strange error when I'm trying to add "unique index" to this table (looks that I have some records violating this).

But I should definitely give it a try and measure how fast if would become.

Btw, I import 500 records at a time, are there any limitation on the SQL query length?

P.S.: there was a point in past, when I simply used REPLACE INTO …but it kept increasing autoincremental ` and that was also "no an ORM" :-)

pilat started a new conversation Performant Way To Mass "update Or Create" • 11 months ago

Hi, I'm looking for a way to do something like updateOrCreateMany (imaginary method).Currently, the code looks like this:

class ActivityRepositoryEloquent extends BaseRepository implements ActivityRepository
{
    // ... before this method, that was something like:
    //  $this->model
           ->whereIn('element_id', array_column($records, 'element_id')
           ->update([ 'delete' => true ]);

    public function insertMany($records)
    {
        $searchFields = [
            'subdomain',
            'element_type',
            'element_id',
            'slug',
        ];

        foreach ($records as $record) {
            $record['deleted'] = false;

            $this->model->updateOrCreate(
                array_only($record, $searchFields),
                array_except($record, $searchFields)
            );
        }
    }
}

What it does, in fact, is the following: it makes SELECT * …nd then `UPDATE ?or each of the records!

Is there a way to minimize the number of SQL queries?

A comment about that 'deleted' field: I'm syncing data with a 3rd-part service. For the original data, let's call is "leads", there is id field defined by 3rd-party system. So, I simply store it in my database with that ID and have no issues. There is related data, however, like "custom fields", that I (re)generate in my site, basing on the leads data. Here I have auto-incremental id and I've noticed that my genius schema (deleting all related and re-generating it after leads sync) causes ids to grow higher and higher after each sync.

So, the revised schema is the following:

  1. Set deleted = false for all the custom fields, with element_id IN ([ array of lead ids ])

  2. updateOrCreate custom fields, basing on leads data + hardcoded deleted = 0

  3. Delete all the records that have deleted = 1 remaining;

22nd August, 2017

pilat left a reply on How To Retrieve Raw Url Query String • 11 months ago

http_build_query($request->query()); // for GET params only
// or
http_build_query($request->input()); // for all data

Not only this would give you "query string, formatted for direct using in links", it would also prepare it in accordance to all related RFCs: eliminate duplicates, encode non-latin characters and so on.

16th August, 2017

pilat left a reply on Design Question: Controllers, Request Processing • 1 year ago

The very first dependency is $subdomain. It's just a string, reflecting currently selected "account". This is an account in a 3rd-party CRM system, in fact, and every user of my Laravel-based site is expected to belong to one or another (or even to several) such accounts. And, even if a user belongs to several accounts, each request to one of "account-aware pages" is bound to one account only (and there must be one).

2nd dependency is, let's call it $api. It is used to communicate with that CRM via API. provides methods like buildApiUrl(), apiCall(), getUsers(), getGroups(), getCustomFields() and many others. I need to know $subdomain when initializing this $api dependency.

3rd one is $conf. It's not an instance of certain class, but rather an stdClass object. I build this $conf once with a command like $this->conf = (object)App::make(\App\Config::class, [ $this->subdomain ])->getAllAsArray(); and then access config variables in $this->conf->var_name fashion. Also, there's separate configuration for each "subdomain".

On the second though, I should probably do the following this time:

  1. Not sure that I even need that parent ServiceAwareController (at least now), considering that I needed it for a common constructor from the very beginning, and then it turned out that controller's constructor was not a very good place to touch Request.

  2. Obtain dependencies from the service container in each action method, just like in "something I'd like to avoid" examples.

I often confuse action methods with other kinds of methods, forgetting that there is only one action method called within the request. So, all that "loading from service container", however "repeating" it looks in the code, will actually be called once per each request, which is good.

  1. Make controllers slimmer. Much slimmer than I have them now. This way I may actually need less dependencies in my action methods.

  2. Then, (after #2 + #3 are done) analyze what is repeating too much and do something with it, if it still itches.

pilat left a reply on Design Question: Controllers, Request Processing • 1 year ago

@martinbean I'm trying to become a bit more DRY about dependencies hooking. Currently, my controller actions look like it's shown in the "would like to try to avoid" examples (both kinds, in fact), but I've realized that the set of dependencies I need inside action methods is almost the same each time ('subdomain', 'conf', 'api_connector', something like this), so, maybe I could move them out of single actions somehow?

pilat started a new conversation Design Question: Controllers, Request Processing • 1 year ago

Hi, I have a number of controllers (but not all), where I need access to specific set of dependencies. Ideally, I'd like to access these dependencies as the class's properties, so, they're like "cached", and they're accessible from any method, closure etc. within that controller.

Here's the meta-code:

class ServiceAwareController extends Controller
{
  protected $subdomain, $dep1, $dep2, $dep3;

  public function __construct()
  {
    $this->subdomain = $request->subdomain;
    $this->dep1 = App::make('dep1', [ $this->subdomain ]);
    $this->dep2 = App::make('dep2', [ $this->subdomain ]);
    $this->dep3 = App::make('dep3', [ $this->dep3 ]);
    // ...
  }
}

class ChildController1 extends ServiceAwareController
{
  public function anActionMethod()
  {
    return $this->dep2->produceSomething();
  }
}

class ChildController2 extends ServiceAwareController
{
  public function anActionMethod()
  {
    return $this->dep1->produceSomething();
  }
}```


The issue: my services depend on request and controller's constructor is not a place to work with request. The request is not fully ready, at this point. This I've read on Github when I've got stuck with "why there is no effect from my middlewares": I've placed some code into the controller's constructor right after `$this->middleware()` calls, and that code presumed all middlewares have already run, which was why it failed.

Is there an elegant solution on how to provide easy access to dependencies to all my "child controllers"? Without overloading action methods' signatures, or putting numerous "App::make" in the beginning of each of them?

I.e., this is something I'd like to avoid, if possible:
```php
class ChildController1 extends ServiceAwareController
{
  public function anActionMethod(Dep1 $dep1, Dep2 $dep2)
  {
    // 1. signature is overloaded
    // 2. there might be some extra logic required to instantiate dependency properly (well, at least I can put that logic into service provider)
  }

  public function anotherActionMethod(Dep1 $dep1, Dep2 $dep2)
  {
    $this->subdomain = $request->subdomain;
    $this->dep1 = App::make('dep1', [ $this->subdomain ]);
    $this->dep2 = App::make('dep2', [ $this->subdomain ]);
    $this->dep3 = App::make('dep3', [ $this->dep3 ]);
    // it won't be very DRY if I put this stuff at the top of each action method...
  }```

15th August, 2017

pilat left a reply on Auth()->check() Inside Error Template • 1 year ago

Why would you want to run abort in a service provider and then still have your application work?

I don't want it to work. I want to display an error page, but to offer some adequate (to the current auth status) links for the user to "get out from this error page".

pilat started a new conversation Auth()->check() Inside Error Template • 1 year ago

Good Morning,

I have an issue with auth() checking inside the error template. Here's what I'm trying to do:

@if (auth()->check())
    <a href="{{ url('home') }}" class="btn btn-default">Home</a>
@else
    <a href="{{ url('register') }}" class="btn btn-default">Register</a>
    <a href="{{ url('login') }}" class="btn btn-default">Login</a>
@endif

Now, if I call abort() inside a controller — the `auth()-check works as expected. If I call `abort from inside a service provider (deferred), however, `auth()->checkalways returns false.

Is there a way to work around this issue?

14th August, 2017

pilat started a new conversation Cannot Test HTTP Responses When Using Abort() Inside A Service Provider • 1 year ago

Hi, an't figure out one thing:

I have the following code in my Service Provider (it's deferred, by the way):

if ($someCond) {
  abort(404, 'blah-blah-blah');
}

… and here's the test:

/** @test */
public function test_regression_404_error_did_not_consider_auth_status()
{
    $this
        ->actingAs(User::find(1))
        ->get('section/somethingimaginary')
        ->seeStatusCode(404)
        ->see('something that only logged user should see on the error page');
}

When I try to run this test, I see the following error in PHPUnit's output:

1) MiddlewaresTest::test_regression_404_error_did_not_consider_auth_status
Symfony\Component\HttpKernel\Exception\NotFoundHttpException: blah-blah-blah

How can I make this test work?

Notes:

  • the service provider is deferred;

  • when trying this same URL in the browser, I can actually see the error page I expect and the browser knows it has 404 response code;

  • there is something conditional (dependent on auth()->check()) on the error page, and it doesn't work, in fact. The error screen is alway as there were no logged in user. When I did abort(403) from a controller, this issue was not here.

11th August, 2017

pilat left a reply on Asserting Array/json Structure Without Doing Any Request • 1 year ago

@JhumanJ this is not what I look for. I need an ability to test my array structure, using the same "template convention" as in seeJsonStructure().

pilat started a new conversation Asserting Array/json Structure Without Doing Any Request • 1 year ago

Hi, I'd like to test array structure, returned by some methods. I like the way how seeJsonSructure() works, but I can't apply it to a "raw data".

Here's example of what I'm trying to do:

        $this->seeJsonStructure(
            [
                '*' => [
                    'id', 'name',
                ],
            ],
            $object->getAllThings()
        );

I understand that seeJsonStructure() should be called on a response, but I don't have separate endpoint for getting just this list. Any workarounds?

2nd July, 2017

pilat left a reply on Should I Use Laravel Notifications For This? • 1 year ago

Ok, it seems I'd better create another kind of App\User, who has extra attributes: emails and phones and who will return these arrays in the routeNotificationFor($driver) function.

Activity class, on the other side, will have functions to return collections of these users. So, sending notifications, related to an activity, would look like this:

Notification::send($activity->getWorkerUsers(), new NotificationForAWorker());

Notification::send($activity->getCusomerUsers(), new NotificationForACustomer());

30th June, 2017

pilat left a reply on Should I Use Laravel Notifications For This? • 1 year ago

#2 seems to be possible to control by via() method:

public function via($notifiable)
    {
        if (!empty($this->opts['debug'])) {
            return [];
        }

        if (!empty($this->opts['channels'])) {
            return $this->opts['channels'];
        }

        return [ 'mail', MySmsChannel::class ];
    }

#4 is also not actual anymore (there's $notifiable as argument to many methods).

However, I need a way to find how to control recipients emails/phones. I need different sets of addresses, depending on either this is notification to Customers or to Workers… Should I make different channels? Or can the recipients list (returned by "Activity:: routeNotificationFor ") be altered somehow from inside the Notification class?

pilat started a new conversation Should I Use Laravel Notifications For This? • 1 year ago

I have a custom entity, let say: "Activity", that bears some data inside and is related to some other data. So, let say, to get notifications recipients, there are methods like: getWorkersEmails(), getCustomersEmails(), getWorkersPhones(), etc.

Now, I need to send various kinds on notifications on some events, related to this activity. For example: ActivityAssigned. To name a few:

  • Activity Assigned notification, sent to Workers by Email;
  • Activity Assigned notification, sent to Customers by Email;
  • Activity Assigned notification, sent to Workers by SMS;
  • Activity Assigned notification, sent to Customers by SMS.
  • ...

Can I use Notification here? Or, is it too complex and I'd rather make special model for this stuff?

Some ideas, in relation to notifications:

  1. They should be fired by $activity instance, because those Workers/Customers are not present in the system as user.

So, the Activity model will be something like this:

class Activity extends Model
{
    use Notifiable;

    // ....

    public function routeNotificationFor($driver)
    {
        if (method_exists($this, $method = 'routeNotificationFor'.Str::studly($driver))) {
            return $this->{$method}();
        }

        switch ($driver) {
            case 'mail_worker':
                return $this->getWorkersEmails();
            case 'mail_customer':
                return $this->getAllContactsEmails();
            case 'sms_worker':
                return $this->getAllWorkersPhones();
            case 'sms_customer':
                return $this->getAllContactsPhones();
        }
    }
}
  1. I think cold group notifications by 'to whom', but implement a series of toChannel() methods inside each of those classes.

  2. I need a way to prevent sending of notifications to selected channels, basing on the $options argument, injected into notification instance. Is is possible at all (besides using exceptions)? Which method should return null or false to have notification to certain channel cancelled?

  3. From inside Notification class, do I have access to the the object, on which ->notify() was called? I.e., instead of: $activity->notify(new ActivityAssignedWorkers($activity, $options)), I'd prefer to have $activity->notify(new ActivityAssignedWorkers($justOptions)).

  4. Is there a way to return something to the place, where $object->notify was called? E.g.: $report = $activity->notify(ActivityAssignedWorkers($options));

pilat left a reply on Using MailMessage In A Mailable Class • 1 year ago

There's a workaround, please check it out: https://medium.com/@woganmay/convert-mailmessage-to-mailable-in-laravel-5-3-d26e74b55c51

I'd like to know if there's a better way, though.

P.S.: here's my version:

// inside mailable's "handle()":

        $message = (new MailMessage)
            ->greeting('Здравствуйте,')
            ->subject($this->subject)
            ->line($this->body);

        return $this->view('vendor.notifications.email', $message->data());

28th June, 2017

pilat left a reply on Tinker Reload? • 1 year ago

In iTerm2, you can open Preferences, then go to Keys, then add a new Key Mapping:

  1. Key Combination: I leave this to your own liking;
  2. Action: 'Send text with "vim" special characters"
  3. The text: exit\nphp artisan tinker\n.

30th May, 2017

pilat left a reply on How Do You Set Up PhpStorm With Existing Laravel + Homestead Project? • 1 year ago

Sorry, I'm not sure that I follow this. I have Valet installed on my machine. Project root is where app, routes, resources, etc. folders are, but web-server root is {projectRoot}/public. I can't find anything appropriate for this scenario in the "Project from existing files" scenario…

Edit Your Profile
Update

Want to change your profile photo? We pull from gravatar.com.