oliverbusk

oliverbusk

Member Since 8 Months Ago

Experience Points 7,550
Experience Level 2

2,450 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 53
Lessons
Completed
Best Reply Awards 0
Best Reply
Awards
  • Start Your Engines Achievement

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • First Thousand Achievement

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • One Year Member Achievement

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • Two Year Member Achievement

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • Three Year Member Achievement

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • Four Year Member Achievement

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • Five Year Member Achievement

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • School In Session Achievement

    School In Session

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

  • Welcome To The Community Achievement

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • Full Time Learner Achievement

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • Pay It Forward Achievement

    Pay It Forward

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

  • Subscriber Achievement

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • Lifer Achievement

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • Laracasts Evangelist Achievement

    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 Achievement

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • Laracasts Veteran Achievement

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • Ten Thousand Strong Achievement

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • Laracasts Master Achievement

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • Laracasts Tutor Achievement

    Laracasts Tutor

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

  • Laracasts Sensei Achievement

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • Top 50 Achievement

    Top 50

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

14 May
1 week ago

oliverbusk left a reply on PHP - Designing A Rule Based Parsing Engine

Hi @bobbybouwmann

I've come to the realization that the content now is not necessarily a string ($content = $document->content;).

I have changed my database setup, to store the value of content as JSON. Now, the content can be either just a string of text, or a multiple columns / rows.

Text:

{"text": "Just a regular string.\n Yep!\n\f"}

Columns/rows: (table data)

{"1": [{"1": "The first line of column 1!\n"}, {"2": "The second..\n"}], "2": [{"1": "Second column\f"}]}

So for the text content, I would just serve the content to the parsing rule like: $document->content['text']

However, I am a bit unsure of how I should serve the column data to the parsing rule method?

For table data, a parsing rule could be:

Text Replace $foo with $bar for all columns (loop through all rows)

Or

Text Replace $foo with $bar for column 1 (loop through all rows)

I am unsure how to do so the parsing rule method can accept both string data and table data? I imagine I would have to do a nested loop through the columns and then the rows? Any help or guidance would be highly appreciated!

oliverbusk started a new conversation Improving A Rule Engine

I am trying to create a simple parsing rule engine, that can parse text according to some parsing rules the user can define.

The problem I've encountered is that my users can currently save text into to my database in two ways:

documents:

id | path           | content
1  | /mydoc.txt     | {"text":"My document text.\nIs awesome!\n\f"}
2  | /another.txt   | {"column":[{"1":[{"1":"A line in column 1.\n"}],"2":[{"1":"Another line.\n"},{"2":"Yet another in column 2\n"}]}]}

So my users can parse a text string text: and column / table rows column:

I have created a class, that can parse rules:

ApplyParsingRules.php;

public function parseContent(array $content, Field $field)
{
    if ($field->rules->count() > 0) {
        $result['text'] = $this->parse($content, $field->rules);
        $result = json_encode($result);
    }

    return $this->data->fields()->attach($field, ['content' => $result ?? null]);
}

/**
 * Iterate through each rule and parse through the content.
 *
 * @return array
 */
public function parse(array $content, object $rules) : string
{
    $results = [];

    foreach ($rules as $rule) {
        $class = $this->getClass($rule);
        $content = $class->apply($content);
    }

    return $content;
}
public function getClass(FieldRule $FieldRule)
{
    $arguments = unserialize($FieldRule->arguments);
    $class = 'App\StreamParser\Parser\ParsingRules\Text\' . Str::camel($FieldRule->method);
    return new $class($arguments);
}

And it is called like:

$Parser = new ApplyParsingRules();
$result = $Parser->parseContent($content, $field);

An example rule could be textReplace.php:

public function __construct(array $arguments)
{
    $this->search = $arguments['search'];
    $this->replace = $arguments['replace'];
}

public function apply(array $content): string
{
    return str_replace($this->search, $this->replace, $content['text']);
}

Above setup works fine. I can provide the $content['text'] from the database, which is basically:

My document text.\nIs awesome!\n\f

However, I would like to allow for this to also parse the column data (for example, only perform text replacement in column 2, or capitalize everything in column 1, row 1.

Any tips / ideas on how I can improve my rule class to accept both the $content['text'] and $content['column'][$i] ?

10 May
1 week ago

oliverbusk left a reply on Laravel - Method With Relationship Query - N+1?

@TRAVISOBREGON - This is exactly what I was looking for! Thanks a lot Travis!

oliverbusk started a new conversation Laravel - Method With Relationship Query - N+1?

Hi there

So I have a website where users can upload documents. When a document is uploaded, it will be saved to a specific folder in my storage, like:

stream_token/document_unique_id/mydocument.pdf

In my Document model, I have created a simple method, that can be used to get the path for the specific document:

    /**
     * A Document belongs to a Stream.
     *
     * @return Illuminate\Database\Eloquent\Model
     */
    public function stream()
    {
        return $this->belongsTo(Stream::class);
    }

    /**
     * Get the path to the unique folder for the document
     *
     * @return string
     */
    public function getPath(string $subfolder = '')
    {
        return "{$this->stream->token}/{$this->unique_id}/{$subfolder}";
    }

Since the method uses the $this->stream->token, every time I use it in my application, like:

 Storage::url($document->getPath('original')

It will fire of a query:

select  * from `streams` where `streams`.`id` = 1 limit 1

I use this method a lot of places in my application, but it seems a bit... Weird to use it, as I know it needs to fire of a query to get the stream->token everytime.

Is there any way I can "pre-load" the token or something like that to improve it?

07 May
2 weeks ago

oliverbusk left a reply on User Specific Settings

@FTIERSCH - @ftiersch Thanks a lot for a detailed response!

I actually just saw that Jeffrey have a 2 part course regarding this exact issue: Managin Mass User Settings

I see that he talks about saving it to a single JSON column like you also suggested. I think this is the way to go!

Thanks! :)

06 May
2 weeks ago

oliverbusk started a new conversation User Specific Settings

Hi all!

I have a web application, where my users can create channels.

This is the basic database table:

channels

id | name | description 

Now I want my users to be able to change different settings for this table, such as:

  1. Channel Font Color
  2. Max number of channel users
  3. Channel Waiting Message

Now I could just add these columns to my channels table:

id | name | description | font_color | max_channel_users | waiting_msg

However, as the project grows the need for more settings also grows - and I can see that my table will quickly become unmanageable.

Is there any other way, in Laravel, where I can specify such settings?

02 May
3 weeks ago

oliverbusk left a reply on Will Event Listeners Run In Sync?

@DEVFREY - In my particular situation, I don't need to present anything to the user - it will be better to queue them and the user can go on with their day - I just need to ensure that the files is executed in the correct order.

Would my solution with calling the jobs (with chaining) from within the listener work and be considered good practice:

protected $listen = [
    DocumetCreated::class => [
       ProcessUploadedDocument::class, 
    ],
];

And then in ProcessUploadedDocument listener file, I can do:


 public function handle(DocumetCreated $event)
 {
        CreateDocumentFolders::withChain([
              new ConvertPDFToImages,
        ])->dispatch();
 }

oliverbusk left a reply on Will Event Listeners Run In Sync?

@DEVFREY - Ah OK, thanks for the clarification.

If I don't use the ShouldQueue interface on my listenres, will the users then have to wait for the entire process to be finished, before their request will finish?

For example, when uploading a new document, will the page then load until both listeneres have finished? (Assuming they run in sync)

oliverbusk left a reply on Will Event Listeners Run In Sync?

OK, but I am only using Events and Listeners at the moment. What would best practice be here? Should I call the jobs from within a listener instead? Like:

protected $listen = [
    DocumetCreated::class => [
       ProcessUploadedDocument::class, 
    ],
];

And then in ProcessUploadedDocument listener file, I can do:

 public function handle(DocumetCreated $event)
 {
        CreateDocumentFolders::withChain([
              new ConvertPDFToImages,
        ])->dispatch();
 }

?

oliverbusk started a new conversation Will Event Listeners Run In Sync?

Hi all

I have a website, where when my users upload document, an event will be fired:

protected $listen = [
    DocumetCreated::class => [
       CreateDocumentFolders::class,
       ConvertPDFToImages::class
    ],
];

As you can see, whenever a document is uploaded and the event is fired, I have two listeners defined.

Both my listeners uses the InteractsWithQueue; trait, so I know they will not run in sync.

However my question is, will

CreateDocumentFolders run first and then when that is finished (return $true), then ConvertPDFToImages will run?

The CreateDocumentFolders will need to run before ConvertPDFToImages.

30 Apr
3 weeks ago

oliverbusk left a reply on Shared Methods In Models

@TALINON - Thanks a lot TALINON! This works perfectly!

29 Apr
3 weeks ago

oliverbusk started a new conversation Shared Methods In Models

I have a blade file, that represents a menu bar. And inside the menu bar, I call a method called path() as below:

menu.blade.php

<li class="nav-item">
     <a class="nav-link" href="{{ $stream->path(['documents']) }}">Documents</a>
 </li>

Now the path method, simple build up a link with the correct path, starting from my /streams url.

    public function path($subpaths = [])
    {
        $path = '';
        foreach ($subpaths as $subpath) {
            $path .= '/' . $subpath;
        }

        return '/streams/' . $this->token . $path;
    }

So for example, {{ $stream->path(['documents']) }} would produce: /streams/documents

Now this works perfectly.

However, as my "subpaths" will ultimately reference other controllers and thus models than StreamsController and Stream, my path method no longer works, without me typehinting the Stream $stream variable in my controller.

For example, inside my StreamDocumentsController.php, I have to do this:

   public function index(Stream $stream, Document $document)
    {
        return view('streams.documents.index', compact('stream','$document'));
    }

Now what is the correct way to share the path() method accross my models, so I don't have to typehint the Stream model in all my controllers`

Can I create some sort of class/trait/interface that can be used accross my models, so they can all inherit the path() method?

27 Apr
3 weeks ago

oliverbusk started a new conversation Queue - "Updated" Doesn't Work

In my model DocumentFieldContent, I have defined below events:

protected $dispatchesEvents = [
     'created' => DocumentFieldCreated::class, //Once document has been uploaded.
     'updated' => DocumentFieldUpdated::class, //One the document fields has been updated.
];

Now, when a new DocumentFieldContent is created in my database, the event is successfully fired, and the listener is picking up the event just fine.

However, the updated event does not work.

Let's say I update the field manually like:

    $update = DocumentFieldContent::find(1);
    $update->content = 'Updated Content';
    $update->save();

The event App\Events\DocumentFieldUpdated is successfully fired, but the listener is not being "catched":

Name                                                            Listeners   
App\Events\DocumentFieldUpdated       0 

I have mapped the necessary things within EventServiceProvider:

    protected $listen = [
        DocumentFieldCreated::class => [
            PerformOCROnDocument::class,
        ],

        DocumentFieldUpdated::class => [
            ParseDocumentFields::class,
        ]
    ];

What I have tried:

I have tried to run below artisan commands:

composer dumpautoload
php artisan clear-compiled
php artisan queue:restart

But it still does not work when I update the resource.

What am I doing wrong?

26 Apr
3 weeks ago

oliverbusk started a new conversation Saving An Array To Database

Hi all

I have a hard time understanding the basic attribute casting in Laravel.

I want to store some coordinates in my database:

$coordinates = [
     'x' => 100,
     'y' => 60,
     'h' => 250,
     'w' => 250,
];

To save the coordinates, I do this:

$field = StreamField::find(4);
$field->coordinates = $coordinates;
$field->save();

I have declared belows casts on my Field model:

 protected $casts = [
     'coordinates' => 'array'
 ];

In my migration, the coordinates column is created like this:

$table->json('coordinates')->nullable();

Now is this the correct way of doing it?

25 Apr
4 weeks ago

oliverbusk left a reply on Advanced Relationship In Laravel

@BOBBYBOUWMANN - Still doesn't seem to work :( I can save the data correctly now, but still can't fetch it like:

$email = Email::find(10);

foreach($email->tags as $tag){
   
//Get the unique tag description for this specific email:
$description = $tag->description;
    
}

Guess this is too complicated for me :( I am on week 4 now and still haven't solved it. Guess it's time to get some professional help.

oliverbusk left a reply on Advanced Relationship In Laravel

@BOBBYBOUWMANN - Maybe I am overthinking this, and don’t need to save the final tag description through a relationship but rather just do something like:

$description = new TagDescription();

$deacription->save([
 ‘tag_type’ => ‘’,
‘tag_id’ => ‘’,
‘typeable_type’ => ‘’,
‘typeable_id’ => ‘’
]);

And then still have the relationship for each document or email, when I need to fetch the tag description for the specific document/email.

What do you think?

oliverbusk left a reply on Advanced Relationship In Laravel

@BOBBYBOUWMANN - Sorry, but no.. My question still remains: how do I get the tag_description for the specific email or document?

Something like:

$email = Email::find(10);

foreach($email->tags as $tag){
   
//Get the unique tag description for this specific email:
$description = $tag->description;
    
}

I just don't know how to achieve that.

Does that makes sense?

24 Apr
4 weeks ago

oliverbusk left a reply on Advanced Relationship In Laravel

@bobbybouwmann , @itellmyselfsecrets

In my web application, I want my users to be able to upload documents and emails to what I call a Channel.

Now I want my users to be able to define documenttags and emailtags on a channel.

  1. All documents uploaded to this channel, will automatically inherit the document tags.
  2. All emails uploaded to this channel, will automatically inherit the email tags.

This means that if there is 50 documents uploaded to the Channel, all 50 documents will have the tags that is created on the specific Channel.

Now the catch is at the tag_description. I will ultimately allow my users to set the content of the tag_description - but this description will vary from document to document, and from email to email.

The users can define parsing rules to the tags, that will ultimately parse the content of the document or email, and add the final content to the tag_description - and that is the reason why a tag_description must refer to the:

  1. Correct tag (email or document tag), and the id.
  2. Specific uploaded type (the specific document or email)

I hope above makes sense?

oliverbusk left a reply on Advanced Relationship In Laravel

@jlrdw I would have no problem with creating an extra field (such as type =email or =document), but I am still unsure how it solves my problem (but I might be overthinking this as @bobbybouwmann said).

If i add a type column to the tag_descriptions table, so it instead look like this:

id | tag_type         | typeable_type     | typeable_id | description
1  |  document     |  App\Document | 55          | My unique description for animals. 
2  |  document     |  App\Document | 55          | My unique description for pets.
3  |  email             |  App\Email          | 20          | Another unique description for animals. 
4  |  email             |  App\Email          | 20          | Yet another unique description for pets.

How would the relationship look like?

@bobbybouwmann

But isn't tag_descriptions the polymorphic table? If I were to use type_id, how would the relationship then look like?

Sorry, but I am a bit (very - lol!) confused.

oliverbusk left a reply on Advanced Relationship In Laravel

@BOBBYBOUWMANN - I can't see how? I cannot see how I can swap either of the polymorphic relationship on tag_descriptions?

Because it needs to reference the type of tag and the id (taggable), as well as the specific type (typeable) - or am I missing something basic here?

Feels a lot more complicated to code, than when I describe it..

oliverbusk started a new conversation Advanced Relationship In Laravel

In my web application, users can upload documents or emails to channels.

A channel can furthermore then have document_tags and email_tags, that all uploaded documents/emails automatically should inherit.

Furthermore, document_tags and email_tags will have different descriptions: tag_descriptions. So for example if we have a document, uploaded to a channel that have the tags: animals (id = 1) and pets (id = 2)

  1. Document #55 is upladed to Channel #8.
  2. Document #55 will automatically inherit the tags, that have document_tags.channel_id = 55 (this can be accessed with the following relationship: $channel->documenttags). In this case animals and pets.
  3. Now the user should be able to set an unique description for the tegs animals and pets in tag_descriptions, for example:

tag_descriptions

id | taggable_type   | taggable_id  | typeable_type | typeable_id | description
1  | App\DocumentTag | 1            |  App\Document | 55          | My unique description for animals. 
2  | App\DocumentTag | 2            |  App\Document | 55          | My unique description for pets.

Now in above database design the uploaded document #55, have the tags: animals and pets associated, but further these two tags have a unique description, that is unique for the specific document.

If I upload another document, or an email (let's say email #20), then I imagine it will look like:

tag_descriptions:

id | taggable_type   | taggable_id  | typeable_type | typeable_id | description
1  | App\DocumentTag | 1            |  App\Document | 55          | My unique description for animals. 
2  | App\DocumentTag | 2            |  App\Document | 55          | My unique description for pets.
3  | App\EmailTag    | 1            |  App\Email    | 20          | Another unique description for animals. 
4  | App\EmailTag    | 2            |  App\Email    | 20          | Yet another unique description for pets.

Now the email #20 also have the tags animals and pets, but in this case, the user can set unique descriptions for the tags.

Now my question is:

Is above design doable, and is it considered best practice in Laravel / PHP? I am a bit unsure how to structure the code, because the TagDescription model will suddenly have two polymorphic relationships (taggable and typeable), and I cannot find anything in the documentation that this is supported.

Furthermore, I am unsure if I can use the above design to access the unique descriptions through the specific uploaded document, such as:

//In my Document.php model:
public function tagdescriptions()
{
    return $this->morphMany(TagDescription::class, 'typeable');
}

Then use it like: $document->tagdescriptions.

23 Apr
1 month ago

oliverbusk left a reply on Multiple Polymorphic Relationship On One Model

@MKBAT - Unfortunately that doesn't solve the problem. Setting default values (or the columns to nullable()) is not a solution, as it will not fix the relationship.

As far as I can tell, one solution is to set the typeable_ relationship manually in the create() function.

$field->result()->create([
  'content' => $content,
  'typeable_id' => $document->id,
  'typeable_type' => get_class($document),
]);

I was just wondering, if there was a cleaner way to save this, so it references two polymorphic relationships

22 Apr
1 month ago

oliverbusk started a new conversation Multiple Polymorphic Relationship On One Model

In my web app, users will be able to save some parsed content to a table called field_results.

However, this model needs to reference two polymorphic relationships:

FieldResults.php:

    // A field result morphs to either a document field or email field.
    public function fieldable()
    {
        return $this->morphTo();
    }

    // A field result morphs to either a document or email.
    public function typeable()
    {
        return $this->morphTo();
    }

The fieldable refers to:

  1. DocumentFields
  2. EmailFields

and the typeable refers to:

  1. Documents
  2. Emails

In my Document, Email, DocumentField and EmailField models, I have defined this relationship:

    // A field will have a final result.
    public function result()
    {
        return $this->morphOne(FieldResult::class, 'fieldable');
    }

So consider below loop, where I need to save some content, that will reference a specific documentfield/emailfield and a specific document/email.

     foreach ($document->fields as $field) {
         $content = $field->content->text;

         //Save the content to `FieldResult
         $field->result()->create(['content' => $content]);
     }

Above will only set the values for the fieldable morph:

SQLSTATE[HY000]: General error: 1364 : Field 'typeable_type' doesn't have a default value [...]

If I change it to save to the document instead, I get below error::

//Save the content to `FieldResult
 $document->result()->create(['content' => $content]);
SQLSTATE[HY000]:  General error: 1364 Field 'fieldable_type' doesn't have a default value [...]

How can I do, so my model FieldResult can have two polymorphic relationships?

oliverbusk started a new conversation Auth Policy, Extend To "sub Pages"

In my web application, users can create "Streams". A Stream have an owner (owner_id).

To only let the owner access the Stream, I have created a policy:

StreamPolicy.php:

  public function view(User $user, Stream $stream)
  {
      return $user->id == $stream->owner_id;
  }

And in my StreamsController.php, I use it through the construct method:

public function __construct()
{
     $this->middleware('auth');
     $this->authorizeResource(Stream::class, 'stream');
}

This works perfectly. When I try to visit a stream, where I am not the owner, I will get a 403 http response.

However, in my Stream, my users can create what I call DocumentFields.

I have a StreamDocumentFieldsController.php, that looks like this:

public function __construct()
{
     $this->middleware('auth');
     $this->authorizeResource(Stream::class, 'stream');
}

public function index(Stream $stream)
{
     return view('streams.documentfields.index', compact('stream'));
}

As you can see, the index method accepts just the Stream, as I have a relationship defined, that show the documentfields in the blade view like:

@foreach ($stream->documentfields as $field)
@endforeach

The problem is, that I can access other Streams documentfield pages, like.

I am the owner of stream: "MyStream". I am not the owner of stream: "YourStream".

I can access this, like I am supposed to:

http://mywebsite.test/streams/MyStream/documentfields

But I can also access this: http://mywebsite.test/streams/YourStream/documentfields

However, when I try to access the other stream directly, like:

http://mywebsite.test/streams/YourStream

I get a 403 error like I am supposed to.

My document_fields table references the stream_id like:

What am I doing wrong?

21 Apr
1 month ago

oliverbusk left a reply on Why Does \Log Not Work In Jobs

I believe you need to change the jobs to run from async to synchronized.

QUEUE_DRIVER=sync

20 Apr
1 month ago

oliverbusk started a new conversation Relationship - Unique Values For Shared Relationship

I am trying to create a web application, where users can:

  1. Upload documents
  2. Upload emails (inbound emails)

The documents/emails will be uploaded to what I call a "Stream". So a stream can have many documents and many emails stored.

However, I am furthermore trying to do so my users can parse the content of the documents and emails, according to a set of parsing rules. The users can create fields that can have many parsing rules.

Example: User A uploads a new document to a stream called "My Documents and Emails". The user have defined below two document fields for this stream:

  1. Order Reference
  2. Tracking Number

Further, there are some email fields that has been defined as well, but will not be used in this case, as User A is currently uploading a new document:

  1. From Name
  2. Email Subject

All fields above will also have some parsing rules, that will parse the content (this is not shown in this example, as it is not relevant).

As you can see, the stream "My Documents and Emails" both have DocumentFields and EmailFields.

This is my current setup:

Stream.php

//A stream can have many document fields
public function documentfields()
{
    return $this->hasMany(DocumentField::class);
}
//A stream can have many email fields
public function emailfields()
{
    return $this->hasMany(EmailField::class);
}

Document.php (Same relationship is avialable are Email.php)

//A document belongs to a Stream.
public function stream()
{
   return $this->belongsTo(Stream::class);
}

//Get the document fields available for the specific stream.
public function fields()
{
   return $this->stream->documentfields();
}

Now my question is, how can I do so the fields are "bound" to the stream, but contain unique value for each Document or Email that is uploaded?

Something like:

document_fields:

id | stream_id  | name             
1  | 1                  | Order Reference 
2  | 1                  | Tracking Number 

document_field_results

id | document_field_id    | document_id | content
1  | 1                                   | 10                    | Lorem Ipsum Dolar Amet for document #10    
2  | 2                                   | 10                    | Some other Lorem Ipsum Dolar Amet for document #10
3  | 1                                   | 55                    | Lorem Ipsum Dolar Amet for document #55       
4  | 2                                   | 55                    | Some other Lorem Ipsum Dolar Amet for document #55      

So whenever a new document is uploaded to the stream (id.1), it will "inherit" the document fields above, but the content will be unique for each field (as each uploaded document/email will ultimately have different content).

What would the correct relationship setup look like in this case? And is this the correct way to go?

10 Apr
1 month ago

oliverbusk started a new conversation Transforming Text Content, Then Persisting To Database [Refactoring]

I have a web application where my users can upload text documents and emails into what I call Streams. A stream is like a "stack", that holds all emails / documents.

My users can then add fields to the stream, and field_rules. This means, that every time a new document or email is added, the text content of the document/email, will be parsed according to the rules, and then the final parsing result is then stored in the database.

My current code works, to some degree, however, it feels a bit "hackish" as well as not very "Laravel like".

My progress so far

Whenever a new document (or email) is added, it will be handled by a queue:

public function handle(DocumentHandlingFinished $event)
{
    $stream = Stream::find($event->document->stream_id);
    $ParsingRules = new ApplyParsingRules($stream, $event->document);

    $event->document->storeResults($ParsingRules->parse());

    return true;
}

OK, so in above I start off by getting the "Stream" that the document was uploaded to.

Then I instanciate the ParsingRule class, that will perform the various rules on the content of the document.

Finally, I save the parsed results to the database.

Below you can see my ApplyParsingRule class:

public function __construct(Stream $stream, Document $document)
{
    $this->data = $document;
    $this->content = $document->content;
    $this->stream = $stream;
    $this->fields = $this->stream->documentfields()->with('rules')->get();
}

//Iterate through each rule and parse through the content.
public function parse() : object
{
    $content = $this->content;
    $results = collect([]);
    foreach ($this->fields as $field) {
        foreach ($field->rules as $fieldrule) {
        
            $content = doSomething($content); //Minified for simplicity.
            $results[] = [
                'field_rule_id' => $fieldrule->id,
                'content' => $content
            ];
            
        }
    }

    return $results;
}

Now as you can see in my handle message, I call the parse() function and then saves the results:

$event->document->storeResults($ParsingRules->parse());

In my Document model, I have the storeResults() function:

Document.php

//Persist the parsed content to the database.
public function storeResults(object $results) : object
{
    //If the document was already parsed before, delete old records.
    if ($this->results->count() > 0) {
        $this->results()->delete($this->results);
    }

    //Use ->last() because we only want to insert the last parsed text result.
    return $this->results()->create($results->last());
}

So above flow works as long as the $results array contains information.

I was wondering if above code can be refactored / improved even further?

Another concern I have is, If the $results array from the ApplyParsingRule class is empty, then the create() method will fail.

03 Apr
1 month ago

oliverbusk left a reply on Improving And Refactoring Code To Reduce Queries

@SALMON - The above code will run behind the scenes/backend, and will be called from an API - not sure if a view model is the best approach? Do you mind giving a simple example?

Thanks!

oliverbusk started a new conversation Improving And Refactoring Code To Reduce Queries

#Improve Request to Reduce Queries

I have a web application, where users can upload Documents or Emails, to what I call a Strema. The users can then define document fields email fields to the stream, that each document/email will inherit. The users can then furthermore apply parsing rules to these fields, that each document/email will be parsed after.

Now let's take the example, that an user uploads a new document. (I have hardcoded the ID's for simplicty).

$stream = Stream::find(1);
$document = Document::find(2);

$parsing = new ApplyParsingRules;
$document->storeContent($parsing->parse($stream, $document));

Below is the function that parses the document according to the parsing rules:

    public function parse(Stream $stream, DataTypeInterface $data) : array
    {
        //Get the rules.
        $rules = $data->rules();
        
        $result = [];
        foreach ($rules as $rule) {

            $result[] = [
                'field_rule_id' => $rule->id,
                'content' => 'something something',
                'typeable_id' => $data->id,
            ];
        }

        return $result;
    }

So above basically just returns an array of the parsed text.

Now as you probably can see, I use an interface $DataTypeInterface. This is because the parse function can accept both Documents and Emails.

To get the rules, I use this code:

//Get the rules.
$rules = $data->rules();

The method looks like this:

class Document extends Model implements DataTypeInterface
{
    public function stream()
    {
        return $this->belongsTo(Stream::class);
    }
    public function rules() : object
    {
        return FieldRule::where([
            ['stream_id', '=', $this->stream->id],
            ['fieldable_type', '=', 'App\DocumentField'],
        ])->get();
    }
}

This will query the database, for all the rules that is associated with Document Fields and the fields, that is associated with the specific Stream.

Last, in my first request, I had this:

$document->storeContent($parsing->parse($stream, $document));

The storeContent method looks like this:

class Document extends Model implements DataTypeInterface
{
    // A document will have many field rule results.
    public function results()
    {
        return $this->morphMany(FieldRuleResult::class, 'typeable');
    }
    // Persist the parsed content to the database.
    public function storeContent(array $parsed) : object
    {
        foreach ($parsed as $parse) {
            $this->results()->updateOrCreate(
                [
                    'field_rule_id' => $parse['field_rule_id'],
                    'typeable_id' => $parse['typeable_id'],
                ],
                $parse
            );
        }
        return $this;
    }
}

As you can probably imagine, everytime a document gets parsed, it will create be parsed by some specific rules. These rules will all generate a result, thus I am saving each result in the database, using the storeContent method.

However, this will also generate a query for each result.

One thing to note: I am using the updateOrCreate method to store the field results, because I only want to persist new results to the database. All results where the content was just updated, I want to update the existing row in the database.

For reference, above request generates below 8 queries:

select * from `streams` where `streams`.`id` = ? limit 1
select * from `documents` where `documents`.`id` = ? limit 1
select * from `streams` where `streams`.`id` = ? limit 1    
select * from `field_rules` where (`stream_id` = ? and `fieldable_type` = ?)
select * from `field_rule_results` where `field_rule_results`.`typeable_id` = ? and...
select * from `field_rule_results` where `field_rule_results`.`typeable_id` = ? and...  
insert into `field_rule_results` (`field_rule_id`, `typeable_id`, `typeable_type`, `content`, `updated_at`, `created_at`) values (..)
insert into `field_rule_results` (`field_rule_id`, `typeable_id`, `typeable_type`, `content`, `updated_at`, `created_at`) values (..)

Above works fine - but seems a bit heavy, and I can imagine once my users starts to generate a lot of rules/results, this will be a problem.

Is there any way that I can optimize/refactor above setup?

01 Apr
1 month ago

oliverbusk started a new conversation Question About Multiple Models In One Controller

Hi all

I have a web application where users can upload documents and / or emails. Furthermore, my users can then create fields for either documents or emails.

For above set up, I have created two models:

document_fields and email_fields.

Now on my frontend, I want to allow my users to Create, Read, Update and Delete fields for either a document or an email.

Esentially, the steps to create a field is identical (give it a name, and save it). Which got me thinking, that I only want one controller for this: FieldsController.php

Basically, if I made two controllers (one for each model), they would essentially contain the same information (CRUD) - but just reference two different tables.

Is there any way that I can use one controller, but use/reference two models? Is this considered good practice?

29 Mar
1 month ago

oliverbusk left a reply on Advanced Relationships

Ah yes, you are right. Do you mind showing an example on how to eager load it using your code where you cycle through? I can only get it to create a query in every each()

oliverbusk left a reply on Advanced Relationships

What about if I eagerload the stream with the fields, like:

$stream = Stream::with(['documentfields', 'emailfields'])->where('id', $stream->id);

$rules = $stream->fieldrules;

foreach($rules as $rule)
{
//Do something
}


And then in Stream.php, I also add this:

public function fieldrules()

{
        return $this->hasMany(FieldRule::class);
 }

oliverbusk left a reply on Advanced Relationships

@FTIERSCH - I am not sure, but I believe so. There are rules that can only be added to email_fields and rules that can only be added to document_fields. Furthermore, each rule applied, is specific for the specific field they are assigned to.

For example, all documents added to Stream #1 should be parsed according to rules: 1,4 and 5. But in another stream, these rules will not be available, as the user must add new rules to new streams.

oliverbusk started a new conversation Advanced Relationships

I have a web app, where users can upload documents or send inbound emails to. All documents and emails are added to what I call a stream. See below setup:

Stream.php

public function documents()
{
    return $this->hasMany(Document::class);
}
public function emails()
{
    return $this->hasMany(Email::class);
}

The inverse relationship has been defined in Document.php and Email.php.

Now, my users can set up custom "fields" for documents and emails. All fields will be the same for all documents inside the stream. Same goes for emails. I have created below two models:

DocumentField.php

EmailField.php

The relationship is defined in Stream.php:

Stream.php

    public function documentfields()
    {
        return $this->hasMany(DocumentField::class);
    }
    public function emailfields()
    {
        return $this->hasMany(EmailField::class);
    }

Now, all fields can have different rules attached to them. A field can have many rules attached, and a rule can either refer to an email_field or a document_field.

To allow for the variable (either email field og document field), I have defined below in EmailField and DocumentField:

    public function rules()
    {
        return $this->morphMany(FieldRule::class, 'fieldable');
    }

And the FieldRule.php I have the morphTo relationship:

    public function fieldable()
    {
        return $this->morphTo();
    }

It seems like I am missing something, because I don't know how I can access the rules for each document or email uploaded to a stream.

For example, I would like something like:

public function parseDocument(Stream $stream, Document $document)
{ 
   //Get the document fields available on this stream.
   $rules = $stream->documentfields->rules;
   $text = document->text;
   foreach($rules as $rule)
   {
       //Do something with rule (specific for document fields on this stream).
   }
}


public function parseEmail(Stream $stream, Document $document)
{ 
   //Get the email fields available on this stream.
   $rules = $stream->documentfields->rules;
   $text = $email->text;
   foreach($rules as $rule)
   {
       //Do something with rule (specific for email fields on this stream).
   }
}
28 Mar
1 month ago

oliverbusk left a reply on Handling Multiple Events

@FTIERSCH - Makes perfect sense! Thanks!

oliverbusk left a reply on Handling Multiple Events

So inside PerformOCROnDocument, would you place the event like this:

$document->save();
event(new OCRFinished($document));

And then in EventSerivceProvider.php, do like this:

OCRFinished::class => [
    ParseDocument::class,
],

?

oliverbusk started a new conversation Handling Multiple Events

I have a web application where users can uploads image documents.

Now, after a new document is uploaded into the system, it should:

  1. Be optimized. (OCR)
  2. Perform various rules on the OCR output.

For this, I rely on Events in Laravel.

I have registered below events for my model Document.php:

protected $dispatchesEvents = [
    'created' => DocumentCreated::class, //Once document has been uploaded.
    'updated' => DocumentUpdated::class, //Once OCR has been performed.
];

Now, for each event, I have also defined listeners.

This is my EventServiceProvider.php:

DocumentCreated::class => [
    PerformOCROnDocument::class,
],
DocumentUpdated::class => [
    ParseDocument::class,
],

So when a new document is created, it will:

  1. Perform OCR on the document. This class PerformOCROnDocument will do various tasks. Most importantly, PerformOCROnDocument will ultimately call:
$document->save();

Since I have defined updated on my Document model, once the save() method is called, it will trigger the next event: DocumentUpdated.

  1. Now, ParseDocument will run, and do some various tasks as well.

Important:

The ParseDocument cannot run before PerformOCROnDocument has finished.

Now my question is: is this the correct way of doing this and handling events? Ultimately, my events will implement ShouldQueue so everything will be added to a queue and handled async.

26 Mar
1 month ago

oliverbusk left a reply on Refactor The Saving Method To Outside The Loop

@SNAPEY - Yes, see below:

App/FieldRuleResult.php


namespace App;

use Illuminate\Database\Eloquent\Model;

class FieldRuleResult extends Model
{
    protected $guarded = [];

    /**
     * A field rule result, belongs to a field rule.
     */
    public function fieldrule()
    {
        return $this->belongsTo(FieldRule::class);
    }

    /**
     * A field rule result, belongs to a document.
     */
    public function document()
    {
        return $this->belongsTo(Document::class);
    }
}

oliverbusk left a reply on Refactor The Saving Method To Outside The Loop

@SNAPEY - Yes, I want to create many FieldRuleResult.

Using

 return FieldRuleResult::createMany($result);

returns below error:

Call to undefined method App\FieldRuleResult::createMany()

oliverbusk started a new conversation Refactor The Saving Method To Outside The Loop

I have below foreach loop, which iterates over a bunch of different custom rules my users can apply.

public function parse(Document $document)
{
        $content = $document->text;
        $rules = $document->stream->fieldrules;

        foreach ($rules as $rule) {
            $class = $this->getClass($rule);
            $content = $class->apply($content);
            
            //Save to database.
            $fieldresult = new FieldRuleResult();
            $fieldresult->create([
                'field_rule_id' => $rule->field_id,
                'document_id' => $document->id,
                'content' => $content
            ]);
        }
}

As you can see, I am calling the database on each iteration. Some users may have up to 50 rules defined, which will then result in 50 insert queries. So I believe I may have encountered a n+1 problem.

I was wondering - what is the best practice here? I've tried searching the Laravel documentation, and found the createMany method. So I tried to refactor the iteration to below:

$result = [];
foreach($rules as $rule)
{
  $class = $this->getClass($rule);
  $content = $class->apply($content);
  $fieldresult = new FieldRuleResult();
  
  $result[] = [
     'field_rule_id' => $rule->field_id, 
     'document_id' => $document->id, 
     'content' => $content];
}

return $rules->createMany($result);

Which gives me below error:

Method Illuminate\Database\Eloquent\Collection::createMany does not exist.

Now I imagine that is because $rules returns a collection. I tried to alter it to:

return $document->stream->fieldrules()->createMany($result);

Which gives me below error:

Call to undefined method Illuminate\Database\Eloquent\Relations\HasManyThrough::createMany()

oliverbusk started a new conversation Refactor A Parsing Rule Setup

I have a web application where my users can:

  1. Upload text documents
  2. Send emails into my application.

Now both Document and Email will contain some kind of text. My users can then parse this text, based on a set of custom parsing rules that they will be able to apply to the text.

The way I have designed it at the moment, is like below. Consider below parsing rule has been applied (they are stored in my database):

field_id | method                | arguments
1        | text_replace            | a:2:{s:6:"search";s:9:"Laracasts";s:7:"replace";s:6:"Google";}

ApplyParsingRule.php

public function parse(Document $document)
    {
        $content = $document->text;

        $stream = $document->stream()->first();
        $fields = $stream->fields()->with('rules')->get();

        foreach ($fields as $field) {
        
            foreach ($field->rules as $rule) {
                //Get the specific parsing rule class for the field.
                $class = $this->getClass($rule);
                //Apply the parsing rule to the content & persist it to the database.
                $content = $class->apply($content);

                $fieldresult = new FieldRuleResult();
                $fieldresult->store([
                    'field_rule_id' => $rule->field_id,
                    'document_id' => $document->id,
                    'content' => $content
                ]);
            }
        }
    
        //Return the parsed text, after all rules have been applied.
        return $content; 
    }

    //Get text parsing rule to apply to the content.
    public function getClass(FieldRule $FieldRule)
    {
        $arguments = unserialize($FieldRule->arguments);

        $class = 'App\MyAppName\Parser\ParsingRules\' . Str::camel($FieldRule->method);

        return new $class($arguments);
    }

Now as you can see, the function will parse the text content through each parsing rule found in App\MyAppName\Parser\ParsingRules\:

textReplace.php

class textReplace implements RuleInterface
{
    private $search;
    private $replace;

    public function __construct(array $arguments)
    {
        $this->search = $arguments['search'];
        $this->replace = $arguments['replace'];
    }

    public function apply(string $text): string
    {
        return str_replace($this->search, $this->replace, $text);
    }
}

Above implements a simple interface: App\MyAppName\Interfaces\RuleInterface.php:

namespace App\MyAppName\Interfaces;

interface RuleInterface
{
    public function apply(string $text): string;
}

Now as you can see, above can only handle the text of a Document - and NOT from Email. Furthermore, I need to apply two foreach loops, in order to get to the field rules.

What can I do to generally refactor above code? It feels a bit.. Dirty and not very "Laravel like". And also, what can I do, so I can have a generic parse() function, that can both parse text from Document and Email?

21 Mar
2 months ago

oliverbusk left a reply on This Forum...

I'd like to weigh in here as well. I'm new to programming and Jeff's series and this community have helped me tremendously!

20 Mar
2 months ago

oliverbusk started a new conversation Dynamically Apply Parsing Rules To Text And Save (Long Read)

OK, so I have been at this problem for the last few days, and I must admit - I am stuck.

I am trying to make a web application, where users can upload documents or send inbound e-mails.

Each document or email will be added to a stream. So a stream holds all the docs/mails.

Now, what I want to allow my users are the following:

  1. Users should be able to add ´fields´ to a stream. A field is essentially just a dynamic variable, that users can set. So for example, if a user uploads a large text file, they can eg. create below fields: 1.1 Order number 1.2 Tracking reference

  2. Users can then add parsing rules to each field. A parsing rule can consist of multiple methods. For example: remove_empty_lines text_replace regex_text_replace Users can apply as many parsing rules to the text string as they want. Everytime a new parsing rule has been applied, the new and updated text string will be stored in parsing_rule_results

OK so above describes the workflow, that a user can setup for each document/email, and above is bound pr. stream.

Now, what I want to achieve is the following:

  1. For each new document/email that is added to my application, I need to check what fields is defined for the specific stream.
  2. For each field defined, I need to run the string through each parsing_rule, and
  3. ultimately, once the the string has been run through each parsing rule, the end result should be dynamically saved to that specific document/email. Something like:

id | field | document_id | email_id | data

1 | 3 | 5 | null | 5000251
2 | 4 | 5 | null | AJIWO4124124J
3 | 3 | 6 | null | 92841
4 | 4 | 6 | null | KKLJPEPQ9102
5 | 3 | 6 | null | E-Order2000
6 | 4 | null | 2 | OOCLTCU8291LK

Which can be translated into:

Stream: 1 Document ID # 5:

  1. order_number= "5000251"
  2. tracking_number = "AJIWO4124124J"

Document ID # 6:

  1. order_number= "92841"
  2. tracking_number = "KKLJPEPQ9102"

Email ID # 2:

  1. order_number= "E-Order2000"
  2. tracking_number = "OOCLTCU8291LK"

Below is the beginning of my database design - but without "field results".

My database setup

This is the code I have so far (models):

Stream.php

    // A stream can have many documents
    public function documents()
    {
        return $this->hasMany(Document::class);
    }

    //A stream can have many e-mails
    public function emails()
    {
        return $this->hasMany(Email::class);
    }
     //A stream can have many fields
    public function fields()
    {
        return $this->hasMany(Field::class);
    }
    //A stream have fields, which then have parsing rules.
    public function parsingRules()
    {
        return $this->hasManyThrough(ParsingRule::class, Field::class);
    }

Document.php

    //A document belongs to a Stream.
    public function stream()
    {
        return $this->belongsTo(Stream::class);
    }

    // A document will have fields.
    public function fields()
    {
        return $this->hasMany(Field::class, 'stream_id', 'stream_id');
    }

Email.php

    // An email belongs to a Stream.
    public function stream()
    {
        return $this->belongsTo(Stream::class);
    }

    //An email will have fields.
    public function fields()
    {
        return $this->hasMany(Field::class, 'stream_id', 'stream_id');
    }

Field.php

   // A field belongs to a Stream
    public function stream()
    {
        return $this->belongsTo(Stream::class);
    }

    // A field can have many parsing rules.
    public function parsingRules()
    {
        return $this->hasMany(ParsingRule::class);
    }

ParsingRule.php

    //A parsing rule belongs to a field.
    public function field()
    {
        return $this->belongsTo(Field::class);
    }

I believe the real problem I have lies in my understanding of relationships, and how I should dynamically apply above logic.

Let's imagine I have a class, that will be called whenever a new document/email is being added:

ApplyParsingRules.php

    public function parse(Document $document)
    {    
         $text = $document->text;
         $stream = $document->stream();
         $fields = $document->fields();

         //1. get all $parsing_rules for each $fields.

         //2. parse $text by using each $parsing_rule

         //3. save the end result of $text by document/email specific and field. 

    }

As you can see, I can fetch the stream details as well as the fields for the specific stream.

However - how can I:

  1. Make it so it allows both Email and Document, depending on what is being added? (Above only allows Document)
  2. Run through each field, and subsequently parse through each parsing_rule and save the end result, so it's specific for each document/email

I hope above is somewhat clear. This post got a lot longer than first expected.

19 Mar
2 months ago

oliverbusk left a reply on PHP - Designing A Rule Based Parsing Engine

@BOBBYBOUWMANN - @bobbybouwmann Above makes perfect sense. Especially the catch where you set $content = $this.. as this will ultimately return the string after all rules have been applied.

I have tried to apply it in my code and it works beautifully!

Thanks a lot for your help and reassurance! I am still quite new to Laravel and only programming as a hobby.

I have two follow up questions:

  1. Would it make sense to save the final $content in the database? Maybe on the documents table, in a column called parsed_content. This way, when the user navigates away from the page and back (or comes back later), the final string will be saved and the server will not need to parse it again. I could then maybe compare parsing_rules.updated_at with documents.updated_at to see if any changes was made to the parsing rules (if there were, all documents associated with the Stream will have to be parsed again.). Which leads me to the other question:

  2. Would it make sense to add the actual parsing of the document - parse() - to a job queue? This way I won't flood my server with parsing requests.

oliverbusk started a new conversation PHP - Designing A Rule Based Parsing Engine

Dynamically apply methods/"rules" to documents

I hope someone here can help me/guide me in the right direction. I am currently creating a web application, where users can import a text file, and then programmaticaly apply different methods on the text.

Example

Imagine that an user have imported a text document, that looks like below.

Raw string

Now as explained, I wish to allow my users to perform/apply a range of different methods to above text. They should be able to apply all rules, in any given order. Consider below example where I have perfomed 4 rules to the original text document:

Rules

As you can see, the text is transformed during each rule, as the method should be applied to the text and output the new text string.

Now the users should be able to save these rules, so the next time the user uploads a document to this specific stream. The thought is, that the next time the user uploads a document, these rules should automatically be applied for the document.

My question is, what would be the best approach to allowing my users to do this dynamically?

I will define the methods/rules that a user can perform on the text document - but what's the best approach:

  1. Save the rules to the database
  2. Programmatically apply the rules to each incoming document (parse each document, based on the rules)

My progress so far

So I am a bit lost on where to begin here, but I was thinking something like below.

Streams: A stream is kind of like a "stack" for all documents. I can upload multiple different documents to a stream. I can create multiple streams, which holds multiple rules.

streams table

id | name
  1. Name: the name of the stream. For example "Documents from Acme Inc"

documents table:

id | stream_id | path | content
  1. Stream Id: A Stream will be can have many documents. So each document uploaded to a specific stream, will be parsed by the rules defined on the stream.
  2. Path: the server path to the document
  3. Content: The text content of the document

parsing_rules table

id | stream_id | method | arguments
  1. Stream Id: Parsing rules will belong to a stream. So all documents imported into the stream, will be parsed by the rules associated with the specific stream.
  2. Rule: the name of the rule applied by the user. This will also refer to the method name in my PHP code.
  3. Arguments: Optional. The arguments that will be applied to each rule/method.

An example of the rules from the 2nd screenshot above, would then look like in the parsing_rules table:

1 | 5 | remove_empty_lines | null
2 | 5 | text_replace | "a:2:{s:6:"Search";s:9:"Laracasts";s:7:"Replace";s:6:"Google";}"
3 | 5 | regex_text_replace | "a:2:{s:7:"Pattern";s:9:"/Google/i";s:11:"Replacement";s:6:"Amazon";}"
4 | 5 | start_position_no_lines | a:1:{s:4:"Line";s:1:"2";}"

So here, method accepts the name of the actual method that should be called, and arguments is the arguments the specific method accepts/requires - but serialized.

#How to apply these rules?

I was thinking that each time a new document is uploaded/imported into a stream, I will apply the rules associated with the stream. Something like:

$content = $document->content;
$parsing_rules = $stream->parsingRules()->get();
foreach($parsing_rules as $rule)
{
    $arguments = unserialize($rule->argments);
    
    return $this->{$rule->method}($arguments, $content);
    
}

Now above is no where near perfect, and it will return the $content already after the first iteration.

Any feedback is highly appreciated. Above is only my thoughts on how to do this project, but I am not sure if there is a better approach to solve this.

oliverbusk left a reply on Validate Incoming E-mail Attachment By Filetype

OK so I fixed this by just checking the File::extension directly on the filename like this:

if (in_array($this->getFileExtension($attachment), ['jpeg', 'jpg', 'bmp', 'png', 'gif'])) {
      $attachments[] = $stream->addDocuments($this->attach($stream, $attachment));
} 

And the method for checking the file extension:

public function getFileExtension($attachment)
{
    return File::extension($attachment->getFileName());
}

oliverbusk started a new conversation Validate Incoming E-mail Attachment By Filetype

I am currently trying to validate incoming emails into my application and save the emails attachments to my database. However, I want to control which attachment types are allowed.

I use Mail-Mime-Parser for PHP, and the Laravel Inbound Mailbox package.

This is my code:

$attachments = collect($email->attachments())->map(function ($attachment) {
    return [
      'file' => $attachment-> getContentType()
    ];
})->validate(['file' => 'mimes:jpeg,jpg,bmp,png,gif,pdf']);

Above returns false, even though the incoming attachment is indeed a pdf file. This is the methods I have tried:

$attachment->getContentType(), whih returns:

Collection {#521
  #items: array:1 [
    0 => array:1 [
      "file" => "application/pdf"
    ]
  ]
}

I have also tried to return the actual content of the PDF file to file, but the validation still fails.

I have also tried to:

[...]->validate(['file' => 'mimetypes:application/pdf']); 

But it still returns false.

What am I doing wrong? How can I successfully validate the attachments mime filetype on the fly?

I am using Spaties Collection Macro for the validate() method.

For reference: This is the actual header content of the attachment, when I dd($email->attachments()):

  -headers: array:5 [
    0 => array:2 [
      0 => "Content-Type"
      1 => "application/pdf; name="Eksamenscase___BIGfinal.pdf""
    ]
    1 => array:2 [
      0 => "Content-Description"
      1 => "Eksamenscase___BIGfinal.pdf"
    ]
    2 => array:2 [
      0 => "Content-Disposition"
      1 => """
        attachment; filename="Eksamenscase___BIGfinal.pdf";\r\n
        \tsize=522462; creation-date="Sun, 03 Mar 2019 14:47:23 GMT";\r\n
        \tmodification-date="Sun, 03 Mar 2019 14:47:23 GMT"
        """
    ]
    3 => array:2 [
      0 => "Content-ID"
      1 => "<[email protected]>"
    ]
    4 => array:2 [
      0 => "Content-Transfer-Encoding"
      1 => "base64"
    ]
  ]
17 Mar
2 months ago

oliverbusk left a reply on Blade - Map Database Values And Show In View

@SNAPEY - @snapey I can't seem to find in the docs, where I can use accessors to achieve my goal? Do you mind providing a short example of what you mean?

oliverbusk left a reply on Blade - Map Database Values And Show In View

@nikos Thanks!

I have added a new file in my langfolder: en/emailrules.php:

    //Fields
    'fromName' => 'From Name',
    'from' => 'From E-mail',
    'toName' => 'To Name',
    'to' => 'To Receiver',
    'cc' => 'CC Receiver',
    'subject' => 'Subject',
    'text' => 'Body',

And I use it like this:

 {{ __('emailrules.'.$rule->field.'') }}

oliverbusk left a reply on Blade - Map Database Values And Show In View

@nikos I wasn’t sure if Translation Strings should be used like this or if there was another way, since the data comes from the database. But will give it a look!