Rants and Raves

Welcome to Rants and Raves! I kept hearing that I needed to write, so here is the blog that I ended up starting. It is mostly an accounting of my progression through creating a web site, nuances with code, and tips about what I did to make things work. In there you will find sprinkled about some gems of life, and letting loose fun. Enjoy!

Laravel Helper Functions Can Do A Lot
2023-02-03
When you need to do things like create a random string, or upper case everything, or any number of formatting functions, make use of Laravel's helper functions: https://laravel.com/docs/9.x/helpers I used the random and upper functions to help me generate fake serial numbers and ended up with something like this: $string = Str::upper(Str::random(10)); https://laravel.com/docs/9.x/helpers#method-str-random https://laravel.com/docs/9.x/helpers#method-str-upper There are a lot of helper functions for strings, arrays, and other stuff too. Definitely worth a look.
Filter Collection With "Where Like"
2023-02-02
When you build a DB query using Laravel's Eloquent you can filter the results using the ->where('', 'like', '') function. A lot of the Eloquent functions work on a collection too, but not this specific variation of ->where(). If you want to pull all of your data from a single query, and then apply a filter of the resultant collection you still can. It makes use of Laravel's ->filter() function, and string comparisons. https://laravel.com/docs/9.x/collections#method-filter https://www.php.net/manual/en/function.stristr.php There does seem to be a lot of potential comparisons that you can use, but the one I went with looks like: $filtered = $collection->filter(function ($item) use ($filter) { return false !== stristr($item->name, $filter); }); https://stackoverflow.com/questions/44103711/collection-where-like-laravel-5-4 Another potential can be found here: https://stackoverflow.com/questions/47413022/laravel-5-5-collection-where-like The one I went with seems to be a good general solution, but you can certainly modify the comparison logic with regular expressions or whatever you need.
Using Switch/Cases To Handle .CSV Data
2023-02-01
With a working data array of your .CSV file you can now save the data to the DB. First, the thing that you will want to make sure of is that any field you want to change using a something like firstOrNew() or firstOrCreate() has that field listed in the fillable section of the Model, similar to: class NewModel extends Model { protected $fillable = [ 'name', ]; } Both of the solutions assumed that the items in the .CSV directly corresponded to the DB fields. A lot of my DB tables have fields that have foreign key references to other tables, so I decided to use switch/case to get the appropriate id information needed to fill out my DB entry. First I would need to loop through the data array of CSV items. Then I would need to loop through each key/value pair within that item and do a switch comparison to determine which ID I was trying to grab. After getting all the IDs I needed I would appropriately find, fill out, and save the item in question. By doing it this way the .CSV columns don't need to be in any particular order and you can still get the right values in the right place. I ended up with something similar to: foreach ($data as $item) { foreach($item as $key => $value){ switch($key){ case('model1'): $model1_id = Model1::firstOrCreate([ 'name' => $value, ])->id; break; case('model2'): $model2_id = Model2::firstOrCreate([ 'name' => $value, ])->id; break; case('model3'): $model3_id = Model3::firstOrCreate([ 'name' => $value, ])->id; break; } } $model1 = Model1::where('id', $model1_id)->get()->first(); $model1->fill([ 'model2_id' => $model3_id, 'model3_id' => $model3_id, ]); $model1->save(); } https://www.scratchcode.io/laravel-switch-case-statement-example/ https://laravel.com/docs/9.x/blade#switch-statements Don't forget to include the "break;" at the end of your case, or it will flow right into the next case as well. I also decided to use firstOrCreate() instead of firstOrNew() because you need to save the new DB entry before it will give you an ID. Create saves it automatically so that way it can return an ID immediately. https://laravel.com/docs/9.x/eloquent#retrieving-or-creating-models
Parse A .CSV
2023-01-31
After you get a .CSV file into your controller, the next thing that needs done is to parse the data into a usable array for Laravel. I used mostly these 2 pages: https://medium.com/technology-hits/how-to-import-a-csv-excel-file-in-laravel-d50f93b98aa4 https://stackoverflow.com/questions/35220048/import-csv-file-to-laravel-controller-and-insert-data-to-two-tables And if you're interested to read up on what Laravel has to say about files: https://laravel.com/docs/8.x/requests#files That first page on medium.com is nice because it walks things through a bit more for a novice, except for using a web interface to upload the file. I like the second page on stackoverflow.com because it provides a more succinct solution for importing a .CSV file, but doesn't have quite as much in validation/error checking. One thing both pages mention/do is make sure to get the .CSV file into the public folder, this is unnecessary, the file is by default put into a temp directory. You can access the current storage path of the file as indicated in the first page with: $tempPath = $file->getRealPath(); and use that to open the file with: $file = fopen($tempPath, "r"); This can allow you to bypass a chuck of code from the first page. While you can access the contents of the file without going through the effort of using the fopen() function, which should be accompanied by the fclose() function, by using something like: $contents= file_get_contents($_FILES['file']['tmp_name']); The results of this are incompatible with the fgetcsv() function that is already available, and used in those 2 pages. https://stackoverflow.com/questions/9101048/php-file-post-upload-without-save As mentioned, I liked the second page for the csvToArray() function that they presented because it produces the key/value pairs in the array that Laravel is very adept at. The downside is that it does require a header in your .CSV, which the first page defaults to ignoring the first row. Another thing both pages do is to initialize an array before looping through the file to fill out the array. At least for the second solution, this is unnecessary, Laravel will still create the array with the first loop. My version looks a bit like this: $file = $request->file('designUpload'); $extension = $file->getClientOriginalExtension(); $tempPath = $file->getRealPath(); $fileSize = $file->getSize(); $this->checkUploadedFileProperties($extension, $fileSize); $file = fopen($tempPath, "r"); $header = null; while (($row = fgetcsv($file, 1000, ",")) !== FALSE) { if (!$header){ $header = $row; }else{ $data[] = array_combine($header, $row); } } fclose($file);
A Rest Area With A Fun Feature
2023-01-30
One of our quick bathroom breaks today was at a rest area in Arizona along I-40. Generally there's not a lot going on, but they are trying to make them a bit nicer of a stop now. This one had a rather fun rock formation that seemed like it could be a lot of fun to climb over. I guess it was more of a plateau no more than 8 feet tall with lots of big smooth rocls around the edges that made for really easy climbing. This feature was decent sized too, it spanned an area that could have fit all of the structures around inside of it. It was a shame that it's so cold and we couldn't stay long, I'm sure there are a couple in this family that would have enjoyed it. It reminded me about the rock climbing gym, and that I do miss spending time there. Oh well, another day perhaps.
RRRAAAAAAVVVEEEE!!!!
2023-01-29
SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH SCHT-DIGUDUH DIGUDUH DIGUDUHDUHDUH
File Upload Requirements
2023-01-28
Started working on handling imports to quickly add a list of things to the DB, useful if you are given the list. The start is of course to have a list, of which the most common is the .CSV file. Then you need to have a form on the website for people to submit this list, using Bootstrap it will look something like this: <div class="mb-3"> <label for="formFile" class="form-label">Default file input example</label> <input class="form-control" type="file" id="formFile"> </div> https://getbootstrap.com/docs/5.2/forms/form-control/#file-input Unfortunately what they neglected to mention to include in their example is that to make sure there is the "name" attribute in the input for use on the back end. They, as well as all of the sites I visited, also forgot to mention that the form element also needs the attribute: enctype="multipart/form-data" https://techsolutionstuff.com/post/how-to-file-upload-in-laravel-9-example https://stackoverflow.com/questions/65937433/unable-to-upload-files-in-laravel-8 The file won't show up in the controller if those 2 things are not present. Plus Laravel needs the @csrf blade directive in the form for security. As part of this process I also rearranged the components of this form into a horizontal line using Bootstrap's grid layout. I made use of the "...-auto" modifier on the "col" class to make sure the column wasn't any bigger than the content inside needed. Also the "justify-content-..." on the "row" class to make sure the columns inside were where I wanted them to be. I ended up with something like this: <form method="POST" action="/path" enctype="multipart/form-data"> @csrf <div class="row justify-content-end"> <div class="col-auto"> <label for="upload_id" class="form-label">Label Text</label> </div> <div class="col-auto"> <input class="form-control" type="file" id="upload_id" name="upload_name"> </div> <div class="col-auto"> <button type="submit" class="btn btn-primary">Upload</button> </div> </div> </form>
Uninstalling Office Click-To-Run Is A B*tch
2023-01-27
If you need to remove the Microsoft Click-to-Run Office BS in order to install a volume license version for your business, you might run into problems. I ultimately started by running the windows uninstaller via the Apps & Features settings. This seemed to work, but when I ran the installer for Office I ended up with an error about a Microsoft Office extensibility component that was preventing another install. I ended up up using this post to find and get rid of that part: https://answers.microsoft.com/en-us/msoffice/forum/all/office-16-click-to-run-extensibility-component/d54dbec3-7fc2-4e79-8fea-aae5d68a7bea#:~:text=Press%20Win%20%2B%20R%20to%20open,the%20folder%20in%20File%20Explorer.&text=3.,4. However, I figured while I was at it I might as well try to remove the program manually to further reduce the possibility that some lingering component was causing issues... of course it wasn't going to be easy. Thank you Microsoft. Luckily Microsoft also has a rather nice manual uninstall guide that worked pretty decently: https://support.microsoft.com/en-us/office/manually-uninstall-office-4e2904ea-25c8-4544-99ee-17696bb3027b?ui=en-us&rs=en-us&ad=us I did run into some issues with deleting some of the files, as they were "in use" by explorer. In the guide they did mention possibly having to restart the PC in order to clear up some of those locks, I was able to free them up by ending the process tree for explorer.exe and starting it back up. WARNING - by ending the process tree any other program you have open will also be closed, so make sure you have anything saved that needs it. After walking through that guide I tried the install again but it ran into another issue, OneNote was "still there". At this point everything else was so demolished that, even after restoring the folder containing the uninstall executable, it could not be uninstalled via Apps & Features. First thing I did from here was to manually delete the OneNote folder in the Program Files (don't remember which one). The second thing was to remove the registry key to show that it was installed, which I got from here: https://support.microsoft.com/en-us/topic/how-to-manually-remove-programs-from-the-add-remove-programs-list-49494159-d215-07b2-e4c7-050457b38352 From here you should be able to do some fancy administrative command line magic to use your multiple activation key (MAK) to activate office. Using command line, CD to "C:\Program Files (x86)\Microsoft Office\Office16" or "C:\Program Files\Microsoft Office\Office16" or something similar. Then run: cscript ospp.vbs /inpkey:*****-*****-*****-*****-***** cscript ospp.vbs /act And tada! Your Office is now activated with your MAK. I did need to re-enter the key upon starting one of the Office products, but I'm not sure if that is because I didn't give the MAK enough time to propagate or something. Not where I got my little script goodies, but some more on MAKs: https://learn.microsoft.com/en-us/deployoffice/vlactivation/activate-office-by-using-mak#enable-a-non-admin-user-to-activate-office-by-using-mak
Two Collapsed Menus
2023-01-26
I wanted to help make the navbar a little bit more mobile friendly and clear that there are more admin links when logged in on mobile devices. To do this I opted to separate the organization links for admins into a second collapsible menu. Also, wanted to keep the desktop view so that those same links are in the middle of the navbar. It would be easy enough to do one thing or the other, but both was a little more tricky. As earlier discussed I was able to split the links of a collapsible navbar with: <button ... data-bs-target="#collapsibleNavbar1">...</button> <div ... id="collapsibleNavbar1"> <ul ... justify-content-start"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> <ul ... justify-content-end"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> It's easy enough to add another group in the middle as such: <button ... data-bs-target="#collapsibleNavbar1">...</button> <div ... id="collapsibleNavbar1"> <ul ... justify-content-start"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> <ul ... justify-content-center"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> <ul ... justify-content-end"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> Or to add a second collapsible navigation like so: <button ... data-bs-target="#collapsibleNavbar1">...</button> <button ... data-bs-target="#collapsibleNavbar2">...</button> <div ... id="collapsibleNavbar1"> <ul ... justify-content-start"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> <ul ... justify-content-end"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> <div ... id="collapsibleNavbar2"> <ul ... justify-content-center"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> But to combine the two ideas I found the use of multi-collapse the answer: <button ... data-bs-target=".multi-collapse" aria-controls="collapsibleNavbar1 collapsibleNavbar3">...</button> <button ... data-bs-target="#collapsibleNavbar2" aria-controls="collapsibleNavbar2">...</button> <div class="collapse navbar-collapse multi-collapse" id="collapsibleNavbar1"> <ul ... justify-content-start"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> <div class="collapse navbar-collapse" id="collapsibleNavbar2"> <ul ... justify-content-center"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> <div class="collapse navbar-collapse multi-collapse" id="collapsibleNavbar3"> <ul ... justify-content-end"> <li class="nav-item"> <a class="nav-link" href="...">...</a> </li> ... </ul> </div> With these ordered in this way on mobile the 1st and 3rd nav menus will be under the left side collapsed menu, and the 2nd nav menu will be under the right side collapsed menu. On desktop they will display 1st, 2nd, then 3rd across the navbar from left to right. https://getbootstrap.com/docs/5.2/components/navbar/#toggler https://getbootstrap.com/docs/5.2/components/collapse/#multiple-targets
Laravel Loops
2023-01-25
A little reminder about the various ways you can loop in Laravel. I know I've been using the foreach() loop a lot, but it is based on the for() loop. You just have to define a little more specifically the loop conditions. There is also the While() loop, which will loop until it's check is true. And a newer to me one of forelse() which is setup like the foreach() but allows for an empty catch. @for ($i = 0; $i < 10; $i++) The current value is {{ $i }} @endfor @while (true) <p>I'm looping forever.</p> @endwhile @foreach ($users as $user) <p>This is user {{ $user->id }}</p> @endforeach @forelse ($users as $user) <li>{{ $user->name }}</li> @empty <p>No users</p> @endforelse https://laravel.com/docs/9.x/blade#loops
Id Title Body Post At