oliverbusk

oliverbusk

Member Since 10 Months Ago

Experience Points 9,030
Experience Level 2

970 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 60
Lessons
Completed
Best Reply Awards 0
Best Reply
Awards
  • start-engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-session Created with Sketch.

    School In Session

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

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

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

  • subscriber-token Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer-token Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • lara-evanghelist Created with Sketch.

    Laracasts Evangelist

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

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

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

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

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

15 Jul
2 days ago

oliverbusk left a reply on Managing Mass User Settings

That gives me:

Trying to get property 'notes' of non-object

And doesnt' really make use of the function that Jeffrey creates in the video I guess?

I have further defined a cast on my model:

    protected $casts = [
        'settings' => 'json'
    ];

oliverbusk started a new conversation Managing Mass User Settings

So I just finished the two part videos https://laracasts.com/lessons/managing-mass-user-settings-part-2.

I have implemented what Jeffrey is talking about - but I am not using the mass settings on an user, but on what I call a Stream.

So a user can create multiple streams, that all can have specific settings.

My question is when registering the settings() function in the ServiceProvider:

$this->app->singleton('App\Settings', function() {
            return User::first()->settings();
});

Jeffrey is simply returning the first user (or this can be changed to return the authenticated user).

However, my question is - how can I implement this, so I can use it on a specific stream?

In my Stream model, I have this:

    /**
     * The settings class, that is used to store mass settings for a Stream.
     */
    public function settings()
    {
        return new Settings($this->settings, $this);
    }

This means I can use it like this:

$stream->settings()->notes;

However, I am not sure if this is the right approach? For example, how can I use it within a blade file?

Doing this:

{{ $stream->settings()->notes }}

I get below error:

htmlspecialchars() expects parameter 1 to be string, array given

oliverbusk left a reply on Caching A Specific Column In Relationship?

The stream token is created when a user created a stream and will never be changed for the specific stream. I guess a path column makes sense! :)

oliverbusk left a reply on Caching A Specific Column In Relationship?

So simply store the entire actual path on the documents table instead? And just reference that directly?

$document->path

?

oliverbusk left a reply on Caching A Specific Column In Relationship?

But as a session is unique to each user, and the stream token will never change - wouldn't it be slower to create a session every time?

oliverbusk started a new conversation Caching A Specific Column In Relationship?

I have a model called Document, in this I have a method that I use throughout my website to get the full path to the specific document:

public function getDocument($documentName = true)
{
    $documentName = $documentName ? $this->name : null;

    return storage_path('app/' . $this->getPath() . $documentName);
}

public function getPath()
{
    return "{$this->stream->token}/{$this->unique_id}/";
}

I use it like this (example):

$documentPath = $this->document->getDocument();

However, as you can see, the document path is made up of:

$this->stream->token/$this->unique_id/

Where $this->stream->token is calling a relationship in my Document model.

public function stream()
{
    return $this->belongsTo(Stream::class);
}

Now the method works - I can use the getDocument() to get the full path to the actual file (or just the folder, if I pass in false).

However, this code is fired out multiple places at different times - and because the folder structure is dependant on the stream_token, it makes a query to get the associated stream on each request.

Solution(s)?

I have tried adding a $with = ['stream'] property to my Document model, so the associated stream is always loaded when fetching the model - but this I don't believe is a good option either, since then all the other queries I have for the document model, that doesn't care about the stream property get's an extra query.

I was wondering - is it possible to cache the token column from the Stream model? Since this column will never change.

14 Jul
3 days ago

oliverbusk left a reply on Relationship BelongsTo Two Columns

Reading the documentation, a many to many relationship seems like the right choice. However, without a model for the pivot, I am not able to use the features of a model - such as $casts. Can I still create a Model file for the pivot?

oliverbusk started a new conversation Relationship BelongsTo Two Columns

I have three models:

  1. File
  2. Field
  3. Result

A file can have many fields, and a field can belong to many files. This gives me:

Files.php

public function fields()
{
    return $this->hasMany(Field::class);
}

Fields.php

public function files()
{
    return $this->belongsToMany(Files::class);
}

Now, my users can upload files to my website and create fields. All files uploaded will "inherit" the fields.

A field could, for example, be called Invoice Number. My user can then add the invoice number from the uploaded file to the field.

However, as each uploaded file is unique, this means that the final field content will also be unique, thus requiring me to create a third model result.

I am not sure how to define this relationship because a result belongs to a field and a file.

Example

files

id | name
1  | invoice1.pdf
2  | invoice2.pdf

fields

id | name
1  | Invoice Number

results

id | file_id | field_id | content
1  | 1       | 1        | #1234
2  | 2       |1         | #8888

I would then like to be able to get the result for the field, for a specific file, such as:

Filename = invoice1.pdf Fields = Invoice Number Results = #1234

10 Jul
1 week ago

oliverbusk started a new conversation Creating A Correct JSON Output That Follows JSON:API Convention

I am trying to parse some JSON that is stored in my database, transform it and then send it to a 3rd party API (by webhook). I am currently stuck at the JSON output format. I am trying to follow the standards of JSON:API.

This is my input from my database column fields.content:

[{"0": "Page 1, col 1.", "1": "", "2": ""}, {"0": "", "1": "Page 1, col 2.", "2": ""}, {"0": "", "1": "", "2": "Page 1, col 3"}]

As you can see, this is a JSON array that consists of objects. Each object represents a row, and each key represents a column. This can be visualed like:

________________________________________________
| COL 1         | COL 2          | COL 3                     |
________________________________________________
| Page 1, col 1.|                         |                          |
|------------------- |--------------------|---------------------|
|                         |Page 1, col 2.  |                         |
|--------------------|--------------------|---------------------|
|                         |                         | Page 1, col 3. |
-------------------------------------------------------------------

In my model Field.php, I use Laravel casting like:

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

Which automatically converts the json string to an array:

dd($content) //$content is the json string from the database

Returns:

array:3 [▼
  0 => array:3 [▼
    0 => "Page 1, col 1."
    1 => ""
    2 => ""
  ]
  1 => array:3 [▼
    0 => ""
    1 => "Page 1, col 2."
    2 => ""
  ]
  2 => array:3 [▼
    0 => ""
    1 => ""
    2 => "Page 1, col 3"
  ]
]

So consider that I do something with this array, like performing a replace on the word Page to Section:

$out = [];
foreach ($content as $col => $rows) {
    $out[$col] = str_replace('Page', 'Section', $rows);
}
dd($out);

This returns:

array:3 [▼
  0 => array:3 [▼
    0 => "Section 1, col 1."
    1 => ""
    2 => ""
  ]
  1 => array:3 [▼
    0 => ""
    1 => "Section 1, col 2."
    2 => ""
  ]
  2 => array:3 [▼
    0 => ""
    1 => ""
    2 => "Section 1, col 3"
  ]
]

I now want to update my database fields.content, to reflect this change. However when re-saving it to the database like:


$field = Field::find(1);
$field->content = $out;
$field->save();

It is now saved as an array of arrays:

[["Section 1, col 1.", "", ""], ["", "Section 1, col 2.", ""], ["", "", "Section 1, col 3"]]

This means that when I send this through my webhook, it no longer follows the same JSON schema like it started out with.

I have tried to json_encode the array, like:

$field->content = [json_encode($out, JSON_FORCE_OBJECT)]

But this doesn't produce the desired output/valid JSON.

Can anyone help me on how to transform my JSON object with Laravel/PHP, and re-save it to my database and keeping the initial valid JSON:API format?

08 Jul
1 week ago

oliverbusk left a reply on Select Model With Relationship (limit Relationship Columns)

I am not sure how I can pass the Stream $stream object from my Vue file? Because $stream is only the ID.

oliverbusk started a new conversation Select Model With Relationship (limit Relationship Columns)

I have a controller called SelectDocumentsController.php with one method index:

I am trying to return the latest 10 documents, where stream_id equals $stream_id in the URL. I have below AXIOS get:

getDocuments() {
    axios
        .get("/api/editor/stream/" + this.$parent.current.stream_id + "/documents")
        .then(response => {
            this.output = response.data;
        })
        .catch(function (error) {
            console.log(error);
        });
}

In my API file I have below route:

Route::apiResource('editor/stream/{id}/documents', 'Editor\SelectDocumentsController')->only(['index']);

And this is my index method:

public function index($stream_id)
{

    return Stream::with(['documents' => function ($query) {
        $query->select('name');
    }])->findOrFail($stream_id);
    
}

I only want to return the column name from the documents - and I don't really need the columns from the Stream model.

Above index method retuns all columns from Stream, but the documents relationship is empty.

What am I doing wrong?

20 Jun
3 weeks ago

oliverbusk left a reply on Vue - Parse JSON String

OK so I played around a bit more.. This is what I came up with:

{
   "1":[
      {
         "text":"Some text here on first column."
      },
      {
         "text":"2nd column text."
      },
      {
         "text":"Even more text. But on the third column"
      }
   ],
   "2":[
      {
         "text":"And more text. Second row."
      },
      {
         "text":""
      },
      {
         "text":""
      }
   ],
   "3":[
      {
         "text":"Text!"
      },
      {
         "text":""
      },
      {
         "text":""
      }
   ]
}

And the vude code:

<table class="text-left w-full border-collapse m-3">
  <thead>
    <tr class="bg-gray-100">

      <th v-for="(item, idx) in this.content" class="p-1">
        {{idx}}
      </th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="(row, rid) in this.content">
      <td v-for="(col, cid) in row">{{ col.text }} </td>
    </tr>
  </tbody>
</table>

Now above code seems to work - it generates the desired output (like in my jsFiddle). However, I have a few questions:

  1. Is this the correct way of saving a "table-like" layout in a JSON string?
  2. In above code, I have an equal number of rows in all my columns (3 rows in all columns). This may not always be the case. For example, column 1 can have 10 rows and column 2 can have 15 rows. In this case, how can I ensure that the visual layout "expands" to 15 rows? Should this be made on the backend (PHP: So I simply add empty "text":"" values to the string) - or should it be made on the frontend?

oliverbusk left a reply on Vue - Parse JSON String

@EMILMOE - @hollyit can either one of you show how the JSON string would then look like? I am a bit puzzled, because in my head, having the columns and then the rows makes sense (that's how you would normally build a table)

19 Jun
4 weeks ago

oliverbusk left a reply on Vue - Parse JSON String

@MAVERICKCHAN - But can't this be achieved, that if there is no row set, it should just be an empty <td></td>?

oliverbusk left a reply on Vue - Parse JSON String

@DUNSTI - This gives me an error:

Unexpected token o in JSON at position 1

Laravel already provides the JSON string.

The problem is not getting the JSON string. I already have that in my view. The problem is parsing it in the table format (like I have tried to do in my JSFiddle manually).

oliverbusk started a new conversation Vue - Parse JSON String

Hi all

I am trying to parse a JSON string that my Laravel application serves to my Vue view. The JSON string can look like this:

{  
   "1":[  
      {  
         "row":"Some text here on first column."
      },
      {  
         "row":"And more text. Second row."
      },
    
   ],
   "2":[  
      {  
         "row":"2nd column text."
      },
      {  
         "row":""
      }
   ],
   "3":[  
      {  
         "row":"Even more text. But on the third column."
      }
   ]
}

Things to note here:

  1. The "1", "2", and "3" refers to columns. So in above examples, I have 3 columns.
  2. Each "row" refers to a row within the column.

I am trying to parse the string as a table, like:

Please see the Fiddle I have created: https://jsfiddle.net/mcxnrft2/ for a visual representation of what I have in mind.

This is what I have now:

<template>
    <div>
      <table>
          <tbody>
              <tr v-for="row in content">
                 <td>{{row}}</td>
               </tr>
           </tbody>
      </table>
   <div>
</template>

<script>
    export default {
        data() {
            return {
  
                content: []
            }
        },
        created() {
            Event.$on("document-was-processed", content => {
                this.content = content;          
            });
        }
    }

</script>

Now above simply prints out the actual JSON string. Can anyone help me out on how to actually parse the content?

Thanks!

oliverbusk left a reply on Controller/Route Design For "frontend Updating"

Thanks both of you!! :)

I've tried the above approach and it actually makes sense and makes it look a lot cleaner.

oliverbusk started a new conversation Controller/Route Design For "frontend Updating"

Hi all

So I have a Vue component called "EditorView.vue". This is a frontend editor, where my users can edit images (crop, resize, add effects etc.)

My editor has two steps:

  1. The image is loaded and the user can do some preprocessing (optimize the image, change DPI etc.)
  2. The "preprocessed" image is loaded, and in this step, the user can do the resizing, cropping etc.

Now when going from step 1 to step 2, the image will need to hit the backend, in order for my server to do the actual preprocessing of the image. The processed image is then returned back to the Vue view and served in step 2.

I've just watched the Cruddy By Design by Adam Wathan, where he talks about implementing the CRUD setup for controllers and keep the number of actions within controllers to a minimal.

I am currently stuck on how to solve this, and how to call the backend.

Let's say I have below controller:

Api\Editor\ImagesController.php

and inside this controller, I have the basic CRUD actions.

I have below route defined:

Route::apiResource('editor/image', 'Editor\ImagesController');

I would then need to have methods such as optimizeImage(), changeDPI() in my step 1, and in step 2, I would need methods such as cropImage(), 'resizeImage()`

However, this has nothing to do with CRUD - it's custom actions to manipulate the image, that I have in my controller.

So what is the best approach to solving this? Should I treat the controllers for my editor differently than the rest?

18 Jun
4 weeks ago

oliverbusk started a new conversation Vue - Async Events Using Axios

Hi all

I am a bit confused on how to use the queue/async event feature within Laravel + Vue.

Let's say I have a Vue component as below:

AddReply.Vue:

<template>
   <div>
      <input value="" name="text" placeholder="Enter text..">
      <button @click='addReply'>Add Reply</button>
   </div>
</template>
<script>
export default {
        data() {
           text: "",
           output: [],
        },
        methods: {
           addReply(){
       
               axios.post("/api/reply", {
                          text: this.text
               }).then(response => this.output = response.data);
   
           }
        }
}
</script>

OK so above is very simplified, but it basically just sends a request to api/reply endpoint.

Now imagine that in my RepliesController.php I fire off an event: SendReplyNotification

This event is queued like below:

class SendReplyNotification implements ShouldQueue
{
    //Do something
}

Now all of this is happening asynchronous. Now, my question is: **how can I, on the frontend, wait for the event/queue to be finished, and then return data back to the Vue view? (AddReply.vue)

For example, I may want to redirect to a new page, after the reply was actually added.

oliverbusk started a new conversation Vue - Preloading JSON String From Database

I am trying to do, so my users can dynamically create table columns on top of an image.

I have below function, which works fine (I have minified the code a bit):

 data() {
     return {
           columns: [],
           xAxis: 0,
     }
},
addColumn: function () {

   this.columns.push({
           id: this.nextColumnId++,
           position: (this.xAxis * 100).toFixed(2),
           xAxis: this.xAxis
    });
},

I can successfully create new columns dynamically:

<a v-on:click="addColumn">Add Column</a>

This will just push a new "column" to the columns array. This will look like:

columns:Array[2]
   0:Object
      position:"30"
      xAxis:72
   1:Object
       position:"60"
       xAxis:578

Now my problem is when I am trying to load an image, that already has columns defined. This is stored in my database like so:

{"1": {"position": "30"}, "2": {"position": "60"}} 

(Above is two columns, at 30% on the page and 60% on the page)

I pass above, to below Vue prop (an object):

current:Object
   columns:"{"1": {"position": "30"}, "2": {"position": "60"}}"
   name:"test"
   created_at:"2019-05-13 17:23:32"

In my child file (DocumentViewer.vue), I try to add these columns:

columns:Object
   1:Object
      position:"30"

   2:Object
      position:"60"

As you can see, the structure in the pre-loaded columns is different from the one created dynamically, thus resulting in my code to break.

My question is, how can I preload the columns as an array, and add each column object, with the correct elements:

position: xx,
xAxis: xx,

oliverbusk left a reply on Web Controllers And API Controllers

@NAKOV - Thanks a lot @nakov! That cleared things up for me :)!!

oliverbusk left a reply on Web Controllers And API Controllers

@NAKOV - Thanks! Saw some of the "Cruddy By Design", and it makes sense!

Just one question to my API route, that currently looks like this:

Route::group([
   'prefix' => 'api',
   'middleware' => 'auth:api'
], function() {

   Route::get("documents/userDocuments", "[email protected]");    

});

Would it make sense to set the namespace as well - and change the endpoint:

Route::group([
   'prefix' => 'api',
   'namespace' => 'Api',
   'middleware' => 'auth:api'
], function() {

   Route::get("documents", "[email protected]");    

});

Like so?

oliverbusk left a reply on Web Controllers And API Controllers

@nakov so in my case, it would be another controller called DocumentsController.php, but placed inside for example Api/ folder - so I will have two DocumentsControllers - one for the web and one for the API?

oliverbusk started a new conversation Web Controllers And API Controllers

Hi all!

So I've found myself stuck in my thoughts... Consider below resource controller:

DocumentsController.php

public function index(){}

public function show(){}

public function store(){}

public function destroy(){}

Which is used to serve the views for the frontend.

Now this works fine. I can perform the basic CRUD actions for the Documents.

However in my app, I have created a new Vue file called

DocumentViewer.vue, that will be used to show specific documents, that belongs to specific users/other parameters.

I would like to fetch the documents by using Laravel/Vue and a simple AJAX call to the backend. Something like:

data() {
     return {
         documents: []
     }
},
created() {
   axios.get("/documents/userDocuments")
             .then(response => this.documents = response.data);
}

As you can see, I have specified a URL called /documents/userDocuments, which may be referenced in the routes/api.php file like:

Route::group([
   'prefix' => 'api',
   'middleware' => 'auth:api'
], function() {

   Route::get("documents/userDocuments", [email protected]);    

});

This is my initial thoughts. However, for some reason it feels a bit.. "non-Laravel" like, and I am not sure if it goes against best practices.

My concerns are:

  1. I have my normal controller CRUD resource endpoints defined inside my routes/web.php file. Would it be OK to place the endpoint for the Vue endpoints within the routes/api.php file - or should this be placed within the web.php file?

  2. I'm a bit unsure about naming conventions in above. I'm referring to getUserDocuments, which will be a function within my DocumentsController - but this goes a bit against the clean controller structure. But on the other side, creating a new controller doesn't seem right either?

14 Jun
1 month ago

oliverbusk left a reply on Dynamically Add/remove Vue Component

It works perfectly! Thanks!

oliverbusk started a new conversation Dynamically Add/remove Vue Component

Hi

I have a Vue component, that can be used to crop an image. I want to be able to allow my users to crop the same images in multiple areas.

Consider below component:

<VueDragResize :w="8" :h="1280" v-on:resizing="resize"></VueDragResize>

I want to be able to add new <VueDragResize></VueDragResize> components dynamically:

<a @click="addComponent">Add Component</a>

<a @click="removeComponent">Remove Component</a>

So whenever I click Add Component link, a new <VueDragResize></VueDragResize> will be created.

I've been searching through SO and Vues forums, but I can't seem to find any examples of how to do this?

Can anyone guide me in the right direction on how to do this?

13 Jun
1 month ago

oliverbusk started a new conversation Conditionally Show Blade View

I have below app.blade.php template:

<!-- Sidebar Area -->
@if (\Request::is('*/fields*'))
    @include('streams.nav.editor-menu')
@else
    @include('streams.nav.menu')
@endif

<!-- Content area -->
@yield('content')

As you can see, up until now I have used below condition, to check whether a specific sidebar should be shown:

@if (\Request::is('*/fields/*'))

Above simply checks if the route contains the fields routes, as defined below:

Route::resource('streams/{stream}/fields', 'Stream\FieldsController');

Now, I've reached a point where I need to use this sidebar on another route resource:

Route::resource('streams/{stream}/documents', 'Stream\DocumentsController');

I could just add this to my if statement:

@if (\Request::is('*/fields*') or \Request::is('*/documents*')) 
...
@endif

However, I suppose down the road that I would need to add even more routes to this statement.

What would be the best approach to this? Would it be possible to maybe declare this in the controller or maybe even the model? Something like:

//Fields.php
/**
 * Specifies whether the editor menu should be shown or not.
 *
 * @return bool
 */

$sidebarEditor = true;

However, I am a bit unsure about how to implement this - or if there is a better approach?

12 Jun
1 month ago

oliverbusk started a new conversation Use Of Constants In A Model

Hi all

I have a basic model called Document. A document can be two things at the moment:

  1. A PDF file
  2. An image file

Currently, I have a column in my documents table called filetype. This column holds the filetype from the uploaded document, like so:

'filetype' => request()->file->getClientOriginalExtension()

However, I've noticed that for a PDF file, for example, the extension can be pdf or PDF.

This is causing me some annoying "bugs" in my code. Imagine:

if ($this->document->filetype === 'pdf') {
 // Do something
}

Now above will works for all PDF files that have the filetype with lowercase pdf.

A workaround could be to just use strtolower($this->document->filetype) === 'pdf') - or not use strict checking.

However this got me thinking - can this be solved by using CONSTS?

For example in my Documents model:

CONST PDF_FILE = 'pdf';
CONST IMAGE_FILE = 'jpeg,jpg,gif';

I know above is not valid code - but I was thinking if something like this was possible within Laravel?

14 May
2 months 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
2 months 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 months 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 months 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
2 months 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
2 months ago

oliverbusk left a reply on Shared Methods In Models

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

29 Apr
2 months 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
2 months 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
2 months 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
2 months 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
2 months 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
2 months 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