<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andrew Schmelyun</title>
    <description>The latest articles on Forem by Andrew Schmelyun (@aschmelyun).</description>
    <link>https://forem.com/aschmelyun</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F143493%2F5f02e70b-0258-4d48-ac23-4480c9d3cc55.jpg</url>
      <title>Forem: Andrew Schmelyun</title>
      <link>https://forem.com/aschmelyun</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/aschmelyun"/>
    <language>en</language>
    <item>
      <title>I built a free tool to help content creators collect ideas and feedback</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sun, 12 Nov 2023 08:03:04 +0000</pubDate>
      <link>https://forem.com/aschmelyun/i-built-a-free-tool-to-help-content-creators-collect-ideas-and-feedback-25gn</link>
      <guid>https://forem.com/aschmelyun/i-built-a-free-tool-to-help-content-creators-collect-ideas-and-feedback-25gn</guid>
      <description>&lt;p&gt;I've been a content creator in the programming space for the last few years, and during that time I've gotten a &lt;em&gt;lot&lt;/em&gt; of ideas from a variety of different sources.&lt;/p&gt;

&lt;p&gt;So I built this: &lt;a href="https://suggest.gg" rel="noopener noreferrer"&gt;Suggest.gg&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the idea came from
&lt;/h2&gt;

&lt;p&gt;For a while, I would add suggestions that I received to a Trello board or Notion database to stay organized, which worked out well enough. It felt clunky though. Other people couldn't see my lists so I regularly got the same idea more than once, and I had no way of knowing what the most popular suggestions were.&lt;/p&gt;

&lt;p&gt;That wasn't a huge deal, I usually just picked out a random idea to work on next and went with it. But as I continued to grow, so did my audience, and the number of requests that I got increased. Moving forward, my goal became to appeal to &lt;em&gt;what my audience wanted to see most&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As an attempt at fixing this, I'd run polls a couple times per month with a few different topics and see what people were most interested in.&lt;/p&gt;

&lt;p&gt;That helped, but got repetitive, so I figured that there has to be a better way to solve this issue. So, I did what any software engineer would do and started building an app to scratch my own itch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Suggest.gg does
&lt;/h2&gt;

&lt;p&gt;Inspired by roadmap applications for software development, Suggest.gg lets content creators organize ideas into different statuses, accept new suggestions, and collect feedback from an audience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqijyxgsxpd7pog5i8vh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqijyxgsxpd7pog5i8vh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You have a &lt;a href="https://suggest.gg/aschmelyun" rel="noopener noreferrer"&gt;public profile&lt;/a&gt; where viewers can see what you're currently working on, what's been suggested by other people, and what has been finished up. They can vote on ideas, and leave more detailed feedback in the form of comments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43swzwuja67wke75pqlc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43swzwuja67wke75pqlc.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You also get a creator dashboard to monitor incoming suggestions, view basic visitor statistics, and manage your existing ideas. You can also customize your profile with a few different themes.&lt;/p&gt;

&lt;p&gt;And well, that's basically it!&lt;/p&gt;

&lt;p&gt;Suggest.gg is &lt;strong&gt;100% free to use&lt;/strong&gt; with unlimited ideas, suggestions, votes, and visits. I built it to serve my own purposes, but wanted to make it accessible to the larger content creator community.&lt;/p&gt;

&lt;p&gt;I hope this helps others like me be less overwhelmed and understand what their audiences are most eager for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I'm working on a pro tier that'll include some additional perks. Things like advanced analytics, multiple boards, and more unique theming options. I'd like to continue working on the platform, improving it in any areas and adding useful features.&lt;/p&gt;

&lt;p&gt;If you're a content creator or know one who might find Suggest.gg useful, I'd love it if you tried it out and tell me what you thought!&lt;/p&gt;

</description>
      <category>showdev</category>
    </item>
    <item>
      <title>The crippling problem of being able to build whatever you want</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Thu, 26 Oct 2023 07:45:52 +0000</pubDate>
      <link>https://forem.com/aschmelyun/the-crippling-problem-of-being-able-to-build-whatever-you-want-i8n</link>
      <guid>https://forem.com/aschmelyun/the-crippling-problem-of-being-able-to-build-whatever-you-want-i8n</guid>
      <description>&lt;p&gt;I currently have 143 different ideas in a backlog waiting to see the light of day, and it is a constant source of anxiety. Maybe you have a similar list of your own, and like me, tell yourself &lt;strong&gt;"if only I had the time to work on them"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I feel that we, as developers, programmers, and software engineers, face a unique problem when it comes to our spare time. We collectively possess the ability to bring our own ideas to life out of thin air, in exchange for nothing but time.&lt;/p&gt;

&lt;p&gt;Once we hit a certain point of knowledge and understanding, it can become trivial to put together cool and interesting projects, open source libraries, and full-fledged SaaS apps. If it's so easy though, why do so many of us struggle to get projects out into the world?&lt;/p&gt;

&lt;p&gt;In this article, I'm going to take a look at some of the common obstacles and origins of these issues, and wrap up with some solutions that helped me build my ideas faster. Let's get into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limiting factors
&lt;/h2&gt;

&lt;p&gt;Talking with people in other professions, like woodworkers or contractors or mechanics, they also have ideas for fun and interesting projects that they'd like to take on in their spare time. This isn't a unique thing to just software engineers or the tech circle. &lt;/p&gt;

&lt;p&gt;These folks that possess the skills and knowledge to make their ideas come to life are usually bottle-necked by something &lt;em&gt;physical&lt;/em&gt;: raw materials, specialty tools, or expensive equipment. They'll likely have to dip into their own pockets in order to get started on a project.&lt;/p&gt;

&lt;p&gt;There are limiting factors, money mostly, and space, that greatly narrows down their focus of potential projects to work on.&lt;/p&gt;

&lt;p&gt;This problem doesn't exist in the software field, or at least nowhere near to the same extent.&lt;/p&gt;

&lt;p&gt;When building a website or app, arguably the largest financial barrier is cost of development. But when &lt;em&gt;you are&lt;/em&gt; a developer, &lt;em&gt;you&lt;/em&gt; possess unlimited quantities of that normally-limiting material.&lt;/p&gt;

&lt;p&gt;It's like a woodworker who lives in a forest of pristine walnut trees or a mechanic who owns a used car lot. Being able to build the software you want cuts out the largest expense, so then you're bound by just one restriction: the amount of time you can reasonably spend working on a project.&lt;/p&gt;

&lt;p&gt;This sounds great! For developers who enjoy building things on the side, if we are able to make it, we &lt;em&gt;can&lt;/em&gt; make it.&lt;/p&gt;

&lt;p&gt;But wait a minute... I have all of these ideas to choose from, which one should I start first?&lt;/p&gt;

&lt;h2&gt;
  
  
  The indecision
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;If I can build &lt;em&gt;anything&lt;/em&gt;, then &lt;strong&gt;what should I pick to work on?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Depending on who you are, this could mentally cripple you to the point of exhaustion. Full stop.&lt;/p&gt;

&lt;p&gt;I mentioned earlier that I have over 140 different ideas for side projects. Here's a little bit of fun math that I did in my head while writing this article.&lt;/p&gt;

&lt;p&gt;If:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only 1 in 3 ideas are viable, interesting projects to build, &lt;strong&gt;and&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Each one takes approximately 2-3 months to get an MVP out, working in my free time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then it would take me over &lt;strong&gt;10 years&lt;/strong&gt; to get through my current backlog of viable ideas. And that's assuming a whole bunch of things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I stop adding my usual 1-2 ideas a week&lt;/li&gt;
&lt;li&gt;I don't get sick, injured, or have a major lifestyle change&lt;/li&gt;
&lt;li&gt;I don't have an increased workload&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that's &lt;em&gt;just&lt;/em&gt; counting development to the point of releasing a minimum viable product. This doesn't include maintenance, support issues, devops, maybe some billing, occasional marketing, urgent security patches, and any other ongoing work that will have to be done if I want to support this project after launch.&lt;/p&gt;

&lt;p&gt;And why wouldn't I? I took the time and effort to create something from scratch, I'd want to see it through til the end. I also don't want to stagnate and disappoint my users or other developers, and I should take pride in what I've built.&lt;/p&gt;

&lt;p&gt;This leads to a new problem, compounding time based on the amount of projects you're maintaining.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compounding interest
&lt;/h2&gt;

&lt;p&gt;In the world of both open source and paid projects, you're bound to have maintenance requests, bug alerts, and other notices coming at you on a regular basis. If you're the sole developer and maintainer of said project, library, or application, then it's &lt;em&gt;your&lt;/em&gt; time that goes into handling these items.&lt;/p&gt;

&lt;p&gt;The more projects you release, the more potential time you lose with recurring support and maintenance. This can easily compound to a point where you're unable to dedicate time to building new projects, and will have to make the decision between dropping your support of an existing project, or not taking up a new one.&lt;/p&gt;

&lt;p&gt;I feel like with each project I take on, I sit and watch as a scale weighs through the pros and cons.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Will I come out of this experience in a positive light when the project is over, or will I be burnt out?&lt;/li&gt;
&lt;li&gt;What if this takes off and I feel obligated to maintain it for years to come?&lt;/li&gt;
&lt;li&gt;If I slow down or stop fixing these issues, will my community react negatively?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These might seem paranoid or excessive to think about for every project that I start, but honestly, I've seen each of the above points come true for multiple different projects throughout my years as an avid open source contributor.&lt;/p&gt;

&lt;p&gt;I've talked enough about the negatives, how about some actions that we can take to actually address the problem. How can we, as intrepid developers, actually complete projects and get finished ideas out into the world?&lt;/p&gt;

&lt;h2&gt;
  
  
  Potential solutions
&lt;/h2&gt;

&lt;p&gt;There's a few techniques I've found over the years that have helped me more consistently move projects into the finished basket on a more frequent basis. Let's check them out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Organize your ideas.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is arguably the most important thing, period. If you're someone like me, new ideas spring up at you all the time. Inspiration for new projects come in the forms of a friend complaining about an existing app, a Tweet asking for a service that doesn't exist, or even passing shower thoughts.&lt;/p&gt;

&lt;p&gt;It's best to get these out of your head, and down onto a stationary media. For me, that's a Kanban board.&lt;/p&gt;

&lt;p&gt;I have four columns for my ideas: Backlog, Promising, Working On, Monitoring, and Archived. If I think of an idea, I create a new card in the Backlog and dump as much info as I can into it. Framework to use, pain points that I could encounter, potential domain names, you get the picture.&lt;/p&gt;

&lt;p&gt;Then, I &lt;em&gt;do nothing&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I don't immediately drop everything to pick up this new shiny project. Instead, I let it marinate outside my mind. If I keep coming back to it days or weeks later, I move it into the Promising column. The next step could be weeks, months, or sometimes years away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't spread yourself thin.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I always have a voice in the back of my head saying "you can squeeze in an extra project, don't worry!". It's a liar. If I decided to take on a new project while trying to balance everything else, something will suffer. There's only so much quality time and work that I can deliver in a day.&lt;/p&gt;

&lt;p&gt;So, I don't pick up anything else until I have the bandwidth to sit down and focus on it. This usually means making the conscious, mindful decision to hold off on any new ideas until something I'm already working on makes it out of the Working On column.&lt;/p&gt;

&lt;p&gt;This is where the root of a lot of my frustrations used to lie. I believed that if I wanted to focus on something else and not stretch myself thin, then I needed to be completely finished with what I was already working on, 100% done.&lt;/p&gt;

&lt;p&gt;But that led to eventual disdain for the projects I was once excited for, so I decided to try something a little different. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release fast and get feedback.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of waiting for a project to be fully complete when I'm working on it, I now tend to &lt;em&gt;release as early as I can&lt;/em&gt;. By getting the bulk of the development work in a "good enough" spot as fast as possible, I maximize my available time that I have to work on projects and can start getting feedback as soon as possible.&lt;/p&gt;

&lt;p&gt;This usually means throwing the project up on GitHub or a purchased domain, writing some halfway-solid documentation, and sharing it out to Twitter, Reddit, and if I'm feeling particularly brave, Hacker News.&lt;/p&gt;

&lt;p&gt;This might make a &lt;em&gt;lot&lt;/em&gt; of people nervous, and it's understandable. The internet, and especially the tech side of it, can be a pretty uncaring place. But, it can also be pretty great.&lt;/p&gt;

&lt;p&gt;At this point, one of two things usually happens for my projects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;I start getting some solid feedback. People are interested in the project or checking it out on their own, and I'm seeing comments, opened issues, and messages.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No one cares and no one really pays attention. This sucks, honestly, and is a pretty terrible feeling. But... it hurts a lot less when you don't sink countless hours into a project perfecting it, only to get a similar response. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If I find out early that I didn't hit a mark with it, I can move on from this project and start something new! If I start getting some feedback though, the project moves onto the Monitoring phase. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iterate and set expectations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Alright, I've got people interested in what I've built, and now I have some actionable feedback coming through, now what? &lt;/p&gt;

&lt;p&gt;For me, at this point the project enters the Monitoring phase. The core functionality is there and valid, and now it's up to me to work on features, fix bugs, or approve pull requests. Keep in mind though, depending on just &lt;em&gt;how&lt;/em&gt; popular your project gets, this could spiral out of hand. So it's best to set expectations.&lt;/p&gt;

&lt;p&gt;Let your community and users know how often they can expect new releases, or how often issues are looked at. You don't have to be a superhero when it comes to response time, and it's an easy way to get burnt out. For me, my goal isn't to make maintaining a project a full-time job in itself, so for a lot of my work I've balanced reponsiveness for new features and issues over working on new projects.&lt;/p&gt;

&lt;p&gt;My users don't seem to mind much and are happy when new releases come through, but you'll have to gauge that for yourself.&lt;/p&gt;

&lt;p&gt;You don't have to use the above like a roadmap and implement everything I've talked about for your own projects and workflow. Every person's different and what works for me might not work for you. Feel free to pick and choose any of these ideas that sound like they might help you out in your current situation, or scrap them altogether and make something new!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I've started and stopped this article multiple times because I just really didn't know &lt;em&gt;exactly&lt;/em&gt; what I wanted to say, it never felt like I was getting my thoughts across well enough. So really, this is just a rant about my particular circumstances and how I've felt creating and maintaining &lt;a href="https://aschmelyun.gumroad.com"&gt;paid&lt;/a&gt; and &lt;a href="https://github.com/aschmelyun"&gt;open source&lt;/a&gt; projects throughout years of my life.&lt;/p&gt;

&lt;p&gt;Just know that if you're experiencing something similar, a conflict between the time you have to dedicate and the desire to build, that you're not alone.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Making the case for Laravel as a Next.js alternative</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Fri, 18 Aug 2023 08:31:32 +0000</pubDate>
      <link>https://forem.com/aschmelyun/making-the-case-for-laravel-as-a-nextjs-alternative-3o5p</link>
      <guid>https://forem.com/aschmelyun/making-the-case-for-laravel-as-a-nextjs-alternative-3o5p</guid>
      <description>&lt;p&gt;&lt;a href="https://nextjs.org" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; is a powerhouse of a full-stack web framework with features like page-based routing, static-site generation, and dynamic React components, but what if I told you that you could get all of that with &lt;strong&gt;just PHP&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F938xnz88tzvnpa363q20.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F938xnz88tzvnpa363q20.jpg" alt="The PHP logo with Next.js replacing the usual text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://laravel.com" rel="noopener noreferrer"&gt;Laravel&lt;/a&gt; is a PHP framework that's been in development for almost a decade. In that time it has solidified itself as one of the most-loved options for building web applications powering the needs of both startups and some of the largest companies around. It also has amassed a massive community of individuals and organizations that provide world-class packages and libraries to enhance its already renown developer experience.&lt;/p&gt;

&lt;p&gt;Within the last year or two, first-party upgrades to the framework and package developers have added a suite of functionality to make Laravel a powerhouse for developing truely full-stack web applications fast and efficiently. In this article I'm going to go through some of the biggest features that Next.js is known for, and offer up examples of similarities found in the Laravel framework. &lt;/p&gt;

&lt;p&gt;This isn't meant to be a "who's better" between the two, but instead a showcase of what Laravel can do out of the box. &lt;strong&gt;Let's start!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Page-based routing
&lt;/h2&gt;

&lt;p&gt;Next.js is famous for its simple and intuitive page-based routing system. But did you know that Laravel has a similar functionality now? Up until this year, if you wanted to create a new route in Laravel you'd have to specify it in a main routes file. However, a first-party package called &lt;a href="https://github.com/laravel/folio" rel="noopener noreferrer"&gt;Laravel Folio&lt;/a&gt; was recently released that enables purely &lt;strong&gt;page-based routing&lt;/strong&gt;, and it works surprisingly similarly to Next.js.&lt;/p&gt;

&lt;p&gt;Let's take a look at an example file structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resources/
└─ views/
   ├─ pages/
   │  ├─ index.blade.php
   │  ├─ uses.blade.php
   │  └─ blog/
   │     ├─ index.blade.php
   │     └─ [slug].blade.php
   ├─ dashboard.blade.php
   └─ welcome.blade.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, anything under the &lt;code&gt;resources/views/pages&lt;/code&gt; follows the convention of page-based routing. So the &lt;code&gt;uses.blade.php&lt;/code&gt; file's content will be visible at our site under &lt;code&gt;/uses&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;An index.blade.php file is used to denote the default root view for that particular directory. That means that the &lt;code&gt;blog/index.blade.php&lt;/code&gt; content will be visible at &lt;code&gt;/blog&lt;/code&gt;. This should seem straightforward and similar to Next.js.&lt;/p&gt;

&lt;p&gt;Except, what about dynamic routes? Well, that's covered too!&lt;/p&gt;

&lt;p&gt;In the above example, you'll see a &lt;code&gt;[slug].blade.php&lt;/code&gt; file. This creates a dynamic route where anything visited under the blog route will be resolved by this view file, with a &lt;code&gt;$slug&lt;/code&gt; variable being included that contains the data passed through to the route.&lt;/p&gt;

&lt;p&gt;For example, visiting &lt;code&gt;/blog/my-example-post&lt;/code&gt; will resolve to that &lt;strong&gt;[slug].blade.php&lt;/strong&gt; page, and if you referenced a &lt;code&gt;$slug&lt;/code&gt; variable in it, will contain the value "my-example-post". This can then be used with an internal function to grab a related model, make an API request, or however else you want to use the value passed through.&lt;/p&gt;

&lt;p&gt;But we can take dynamic routes &lt;em&gt;one step further&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For our blog, if we were using a Laravel model called &lt;code&gt;Article&lt;/code&gt; to hold data for a single blog article, we can actually use route-model binding in our dynamic page-based routes by just renaming that file to &lt;code&gt;[Article].blade.php&lt;/code&gt;. Now inside the blade file, the variable &lt;code&gt;$article&lt;/code&gt; will be available, containing the entire Article model's attributes!&lt;/p&gt;

&lt;h2&gt;
  
  
  Static-site generation
&lt;/h2&gt;

&lt;p&gt;Next.js recommends using static generation whenever possible, and it's enabled by default on most pages. Basically anything that doesn't require heavy user interaction can be statically generated on build time and then served up via your server (or better yet, a CDN).&lt;/p&gt;

&lt;p&gt;By default Laravel generates every route dynamically. It's not slow since there &lt;em&gt;is&lt;/em&gt; a route cache which helps cut down on a lot of the PHP interpretation, but the fact still remains that a Laravel app relies on dynamic markup generation.&lt;/p&gt;

&lt;p&gt;We can change that though!&lt;/p&gt;

&lt;p&gt;There's a package, &lt;a href="https://github.com/spatie/laravel-export" rel="noopener noreferrer"&gt;laravel-export&lt;/a&gt;, that comes from a well-respected third party. After installation there's &lt;strong&gt;zero necessary configuration&lt;/strong&gt; before our entire site can be statically generated with a single command, &lt;code&gt;php artisan export&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;This will crawl our site, following links on pages, and save the rendered markup to a &lt;code&gt;dist&lt;/code&gt; folder in our application's folder structure. It also works well with the Folio page-based routing mentioned above. &lt;/p&gt;

&lt;p&gt;Like with Next.js, we can modify the behavior of this through some small configuration changes in our app.&lt;/p&gt;

&lt;p&gt;We can specify paths to exclude from the crawler, programmatically turn it on or off, and write hooks that fire off commands before or after the export runs (for example, if we wanted to deploy the exported site files to a production server). &lt;/p&gt;

&lt;p&gt;Unlike Next.js, there's no equivalent of a &lt;code&gt;getStaticProps&lt;/code&gt; method that runs when building the exporter. You can always include a function in the body of the Blade template if you're using something like Folio, which will run as each page is compiled, although that'll also occur &lt;em&gt;outside&lt;/em&gt; of the exporting as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server-side rendering
&lt;/h2&gt;

&lt;p&gt;While using Next.js you need to include a &lt;code&gt;getServerSideProps&lt;/code&gt; method to enable SSR, but PHP comes with server side rendering on by default!&lt;/p&gt;

&lt;p&gt;In normal circumstances, PHP is rendered on each request through the server. This means that writing PHP code in your blade files guarantees that the content of your functions will be hidden from the generated markup that comes back to the browser. Those files are never accessed &lt;em&gt;directly&lt;/em&gt;, and so this adds a layer of security to your application by default.&lt;/p&gt;

&lt;p&gt;Going deeper than just blade files, Laravel is a full MVC framework and so includes things like Models and Controllers out of the box that can be used to organize your server-rendered code. Authentication is also baked in by default, and with first-party packages like &lt;a href="https://github.com/laravel/breeze" rel="noopener noreferrer"&gt;Breeze&lt;/a&gt;, &lt;a href="https://github.com/laravel/sanctum" rel="noopener noreferrer"&gt;Sanctum&lt;/a&gt;, or &lt;a href="https://github.com/laravel/socialite" rel="noopener noreferrer"&gt;Socialite&lt;/a&gt;, you can include user registration, login, API-based authentication, social sign-ups, and role-based permissions with near zero configuration.&lt;/p&gt;

&lt;p&gt;A similar out of the box piece to look for is a database. Laravel includes an abstraction layer called &lt;a href="https://laravel.com/docs/eloquent" rel="noopener noreferrer"&gt;Eloquent&lt;/a&gt; that makes it easy to interact with your database of choice. It's a full ORM that can be used to create, read, update, and delete records, and can be used to build out more complex relationships between models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single page components
&lt;/h2&gt;

&lt;p&gt;Some of the beauty of Next.js comes from the fact that you're using the React library, and all of the wonderful developer experience that comes with it. This enables you to easily build dynamic user interfaces and responsive views with relative ease.&lt;/p&gt;

&lt;p&gt;Everything can also be self-contained inside a single page component. Can this be done inside of our Laravel app since everything is server rendered by default?&lt;/p&gt;

&lt;p&gt;Up until recently we've had to install and configure a frontend framework to get that functionality, but that requires a separation of concerns and the maintanence of two different codebases. Instead, we can use &lt;a href="https://laravel-livewire.com" rel="noopener noreferrer"&gt;LiveWire&lt;/a&gt; and Volt to give us dynamic, single-page components with &lt;em&gt;just&lt;/em&gt; PHP.&lt;/p&gt;

&lt;p&gt;Let's say that in our &lt;code&gt;[Article].blade.php&lt;/code&gt; page mentioned way above that after each page's content we have a subscription form. We can use these two packages to build a dynamic component into our existing page using PHP and Laravel. It might look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php

use App\Livewire\Forms\SubscribeForm;
use function Livewire\Volt\{form};

form(SubscribeForm::class);

$save = fn () =&amp;gt; $this-&amp;gt;form-&amp;gt;store();

?&amp;gt;

&amp;lt;form wire:submit="save"&amp;gt;
    &amp;lt;input type="email" wire:model="form.email"&amp;gt;
    @error('form.title') &amp;lt;span class="error"&amp;gt;{{ $message }}&amp;lt;/span&amp;gt; @enderror

    &amp;lt;button type="submit"&amp;gt;Subscribe&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now when our page is visited or rendered, we'll have a form at the bottom that the user can fill out and submit without needing to perform a full page refresh!&lt;/p&gt;

&lt;p&gt;Unlike Next.js though, this interactivity is &lt;strong&gt;dependant on server rendering&lt;/strong&gt;. LiveWire uses JavaScript on the frontend to hydrate these components and provide client-side interactivity, but the core functionality and reactivity depends on the server to manipulate state and perform the functions requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development environment included
&lt;/h2&gt;

&lt;p&gt;With Next.js, you have an included development server that's ran with npm. With Laravel, there's a few different options for a local development environment.&lt;/p&gt;

&lt;p&gt;First, if you have PHP installed locally on your system, then it's as easy as running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;php artisan serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This boots up a local PHP server instance, included in the actual &lt;em&gt;language&lt;/em&gt; itself. It's a bit limited, blocking by nature and without a database, but it gets the job done and is responsive enough to use for 90% of local development cases.&lt;/p&gt;

&lt;p&gt;If you want something more complex with additional features like a MySQL server, Redis instance, or Mailpit box, then you can use the first-party Laravel Sail which comes pre-installed to new Laravel apps. It's a bash script wrapper for Docker that boots up a network of containers and handles your local dev environment setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ease of deployment
&lt;/h2&gt;

&lt;p&gt;Alright, you're ready to release your application to a production environment, now what? Let's assume that we have a mixture of static content and server-side rendered routes, which means we'll need to have a setup that can run our applications dynamically.&lt;/p&gt;

&lt;p&gt;For Next.js, this means that we need to have a server provisioned with Node.js. Something like a basic Amazon EC2 instance or a DigitalOcean Droplet can handle that, and the installation and configuration is pretty straightforward. What about Laravel?&lt;/p&gt;

&lt;p&gt;Since Laravel runs on PHP, and PHP has been around for decades, getting a server provisioned with the requirements for a LAMP (or LEMP) stack is not too difficult of a task. There's a plethora of options available, from shared hosting to VPS providers. Even managed services like &lt;a href="https://forge.laravel.com" rel="noopener noreferrer"&gt;Laravel Forge&lt;/a&gt; that can handle the provisioning and configuration of your server for you, similarly to how Next.js has managed application instances with Vercel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What else?
&lt;/h2&gt;

&lt;p&gt;This article wasn't meant to be a competition between Next.js and Laravel. They're both fantastic batteries-included, full-stack web frameworks that enable you to build applications and ship awesome features.&lt;/p&gt;

&lt;p&gt;However, if you're looking for an alternative to Next.js, maybe with something that's a bit more batteries-included, Laravel could be what you're looking for. With a robust ecosystem, a passionate community behind it, and the ubiquity of a language powering over 70% of the web, it's definitely an option to keep in mind.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>nextjs</category>
      <category>javascript</category>
    </item>
    <item>
      <title>AI is a fad and programming is dead</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Mon, 01 May 2023 05:34:58 +0000</pubDate>
      <link>https://forem.com/aschmelyun/ai-is-a-fad-and-programming-is-dead-180f</link>
      <guid>https://forem.com/aschmelyun/ai-is-a-fad-and-programming-is-dead-180f</guid>
      <description>&lt;p&gt;These are the two prevailing point of views that I’ve seen spread the most on social media.&lt;/p&gt;

&lt;p&gt;On one hand, you have people saying that programming as a future career is essentially dead, and it’ll be a couple of years before humans writing code is made obsolete. Then there are those who think that LLMs like ChatGPT and LLaMA are party tricks, nothing more than a fun little experiment that provides minimal value to those building software.&lt;/p&gt;

&lt;p&gt;I think the reality is more nuanced than that, and both points above can be harmful or anxiety-inducing to people who just started in the software engineering field or are interested in it as a career path. I’d like to take a little time and go over what I think AI will change in this landscape over the coming years.&lt;/p&gt;

&lt;p&gt;If you’d like a quick breakdown or tl;dr of what’s going to be in this article, the main points are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI will continue to improve, probably in an S-curve fashion, but we don’t know where on the curve we currently sit.&lt;/li&gt;
&lt;li&gt;LLMs are unlikely to take jobs away from software engineers, but they will enable teams and individual contributors to be more efficient, and get more done with less time and resources.&lt;/li&gt;
&lt;li&gt;Tools like ChatGPT usually require you to give detailed instructions in a logical format to yield good code results, this is essentially the role of a programmer just in a new, esoteric language.&lt;/li&gt;
&lt;li&gt;If AI improves to the point that humans are removed completely from the software process then we have made it pretty much to AGI, at which point we have &lt;em&gt;much&lt;/em&gt; bigger problems to worry about based on our current societal structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still interested? Let’s continue!&lt;/p&gt;

&lt;h2&gt;
  
  
  AI improvement and the S-Curve
&lt;/h2&gt;

&lt;p&gt;AI has improved drastically over the course of the last year or so. We’ve gone from some basic sketches and drawings to full-fledged generated movies based on images, text, and speech a la Midjourney and others. OpenAI’s improvements to the GPT model has shown incredible promise in generating text and code. There are AutoGPT experiments, where talented developers have been running processes that spawn LLM entities and have them loop over and perform actions to finish complex multi-step assignments.&lt;/p&gt;

&lt;p&gt;All of these might make it seem like we are advancing at an exponential pace in this field, but that might not be the case.&lt;/p&gt;

&lt;p&gt;Technological advancements have historically been shown to happen on something called &lt;a href="https://www.researchgate.net/publication/340525808/figure/fig2/AS:881029480996871@1587065205861/Successive-S-curves-in-technological-development-The-dotted-line-shows-the-overall.png"&gt;successive S-curves&lt;/a&gt;. A technology will slowly advance and then rapidly take off, before slowly plateauing out for a while. A new iteration happens that starts the same cycle over again, but this time at an overall higher level. Zooming out, the graph appears linear but is instead made up of multiple iterations of these curves.&lt;/p&gt;

&lt;p&gt;It’s not a bold assumption that AI might follow the same path (at least for now), so the question becomes “where are we on the current curve?”. Maybe toward the bottom, right before accelerating exponentially up, or towards the top with little room to advance at the moment.&lt;/p&gt;

&lt;p&gt;If I had to bet on a particular point in the line, I’d say that we’re right before the plateau.&lt;/p&gt;

&lt;p&gt;I think we have a small amount of advancement to go with LLMs and the technology that currently surrounds them and AI in general, but I think that we’re missing &lt;em&gt;something&lt;/em&gt; that will take time to fully provide.&lt;/p&gt;

&lt;p&gt;As more and more AI generated content comes out (video, speech, code, images), more and more people can recognize it for its uncanny-valley-ness. Like most projects, I believe that the missing 10% will take 90% of the time and effort to perfect.&lt;/p&gt;

&lt;p&gt;How long until we hit that next curve though, and exponential improvement begins again? That’s difficult to say, and no one person should be able to answer that.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLMs are the next step in IDE advancement
&lt;/h2&gt;

&lt;p&gt;Getting back on track about programming though, we have seen a drastic improvement and adaptation of models like OpenAI’s GPT in the coding world. Tools like ChatGPT, GitHub Copilot, and now Copilot X have improved the efficiency of developers around the globe.&lt;/p&gt;

&lt;p&gt;But, they’re not &lt;em&gt;replacing&lt;/em&gt; programmers, they’re &lt;em&gt;tools&lt;/em&gt; in a utility belt enabling programmers to work better.&lt;/p&gt;

&lt;p&gt;The first time I went from a text editor like Sublime to a full-fledged JetBrains IDE was &lt;em&gt;insane&lt;/em&gt;. My productivity skyrocketed as I suddenly had access to auto-completion, automated refactoring, integrated testing, and deeper connections to my project’s dependencies. I was able to produce cleaner code at a dramatically faster rate.&lt;/p&gt;

&lt;p&gt;These AI tools are just the next logical step in that same improvement process.&lt;/p&gt;

&lt;p&gt;I’ve been using Copilot and ChatGPT in my personal projects since the start of the year, and I have noticed a similar efficiency improvement.&lt;/p&gt;

&lt;p&gt;A lot of work done in new projects are tasks I don’t have to put a lot of thought or effort into, but that take up time. Creating automated tests, frontend templates, new classes, or formatting data outputs, I can complete these items 10x faster now that I have the help of these tools. They just &lt;em&gt;understand&lt;/em&gt; what I’m trying to do, and scaffold out the code necessary in my project to accomplish it.&lt;/p&gt;

&lt;p&gt;Going deeper, there have been times where I need to create a complicated function body or work with a library that I’m not super familiar with. I can type in a basic comment for what I’m trying to build in that instant, and the AI spits out a code block that’s usually helpful in satisfying the requirements I’m after.&lt;/p&gt;

&lt;p&gt;For an example, I was recently in a PHP application and wanted to work with a popular FFMpeg library with pretty complicated documentation. Instead, I just wrote the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;formatVideo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$video&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// use ffmpeg to convert the video to a gif&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hitting enter after I made that comment produced succinct code that I could use in the rest of the project, with a few minor tweaks.&lt;/p&gt;

&lt;p&gt;But the fact of the matter is that &lt;strong&gt;it saved me a ton of time&lt;/strong&gt;. If I had &lt;em&gt;known&lt;/em&gt; the exact details of the library, sure, a minute or two might have been shaved off. But because I was in this realm between knowing the language but not a particular library, I knew exactly what I could ask and could use the code given to me immediately. All without having to take time poring over documentation, Stack Overflow questions, or trial and error.&lt;/p&gt;

&lt;p&gt;This is where these tools and AI for programming really shine, it enables you to work &lt;em&gt;better&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Two things though.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It’s over-confident.&lt;/strong&gt; There have been &lt;em&gt;multiple&lt;/em&gt; times where the AI has hallucinated arguments or functions that don’t exist in my project or a library I’m using. I might be able to nudge it in the right direction after a few retries, but sometimes it goes completely off the rails and writes code that straight up does not accomplish what I wanted it to. This has been pretty rare, though.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It’s not connected.&lt;/strong&gt; ChatGPT and Copilot are great at creating &lt;em&gt;snippets&lt;/em&gt; of code that perform a specific function from given input. An entire application is still a bit out of reach. First, there’s a barrier for the amount of text, as even GPT-4 is limited by 8K tokens (around ~6k words). Second, even with the use of vector databases and AutoGPT, the models have a hard time sticking to a single development style or accomplishing an overall directive in a program that’s just above moderately complex. Ask them to create a todo app and it’s likely to be fully functional. Ask for something like a CRM to handle leads for a barbershop and it’s more likely to start on the right track, but end up with missing functionality or ineffectual code.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last part in particular brings us squarely to our next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Programmers are translators for a logic language
&lt;/h2&gt;

&lt;p&gt;If you ask a lot of people what their definition of a programmer is, you’ll likely get a lot of responses that boil down to “someone who writes code”. And while this &lt;em&gt;isn’t incorrect&lt;/em&gt;, it’s also missing a huge part. A programmer, software engineer, developer, or whatever title you choose, is a &lt;em&gt;translator&lt;/em&gt; for a language that deals in logic.&lt;/p&gt;

&lt;p&gt;Your goal as a programmer is to take a concept, an idea, or a workflow in a language that you can understand, and translate that to a language that a computer can understand. This programming language is designed to prevent ambiguous statements and deal in pure logic.&lt;/p&gt;

&lt;p&gt;Let’s take the sentence “When the button is pressed, change the background to red”.&lt;/p&gt;

&lt;p&gt;If you’re a person in a meeting with other people from your team, you all might intuitively know exactly what is meant by that.&lt;/p&gt;

&lt;p&gt;But if you’re a computer, you have a ton of missing information. What button? What background? What shade of red? What if it’s pressed again?&lt;/p&gt;

&lt;p&gt;We can redefine our sentence again to try to remove ambiguity. “When the button with the ID of ‘clicky’ is pressed, change the background of that same button to a color with the hex value #FF0000”&lt;/p&gt;

&lt;p&gt;Written in JavaScript it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clicky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundColor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#FF0000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re familiar with this programming language, and you were given the code above and asked to explain what it does, you might produce a sentence similar to the second one above.&lt;/p&gt;

&lt;p&gt;You’ve &lt;em&gt;translated&lt;/em&gt; JavaScript into your native language.&lt;/p&gt;

&lt;p&gt;This is the heart and essence of programming, and is one of the biggest reasons I believe that the profession will be around for quite a while, even in the face of advancing AI tools.&lt;/p&gt;

&lt;p&gt;There’s hundreds and thousands of threads online where people ask “Why isn’t ChatGPT producing the code I want it to?” and inevitably the answer comes down to:&lt;/p&gt;

&lt;p&gt;“You need to know how to talk to it.”&lt;/p&gt;

&lt;p&gt;Well, if I need to use a &lt;em&gt;specific&lt;/em&gt; language to talk to this tool and get back accurate data every time, then I’m just &lt;em&gt;programming with a natural language&lt;/em&gt;. This isn’t a new concept, it’s just that with the breadth and complexity that LLMs offer, the barrier for entry is lower. &lt;/p&gt;

&lt;p&gt;Even if building an application has been reduced down to typing in prompts in a tool like ChatGPT, if you have to use a specific language to get it to create a reliable output that works every time, you’re in essence &lt;em&gt;still programming&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Looking at the present, trying to build an application with an LLM with the current limitations of the models means that you’re likely going to have to put some of the pieces together yourself. This still constitutes programming, and you’ll need to know some basics about &lt;em&gt;what&lt;/em&gt; the language is that it produced code for you in and &lt;em&gt;where&lt;/em&gt; to put the pieces it gave you.&lt;/p&gt;

&lt;p&gt;But let’s say that things advance to the point where that’s trivial or useless, because the AI will do it for you anyway. You say “Alright, compile these assets and publish them on example.com”. At that point, why even &lt;em&gt;have&lt;/em&gt; a programming language? Let the AI be the layer between your data and your result, skipping the middle step entirely.&lt;/p&gt;

&lt;p&gt;Well at that point, we’re basically at &lt;a href="https://en.wikipedia.org/wiki/Artificial_general_intelligence"&gt;AGI&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  AGI replaces everything
&lt;/h2&gt;

&lt;p&gt;Let’s say that everything above has been perfected and there needs to be almost zero human intervention needed. You can simply have a prompt attached to a database (also managed by an AI) where you can simply ask for any dashboard, result set, or functionality that you could from a program. What then?&lt;/p&gt;

&lt;p&gt;If AI is powerful enough to remove human intervention and oversight from the programming realm, then it has gotten to the point that it can replace almost every creative and knowledge worker profession in existence. This would likely lead to a worldwide economic downturn as more than 60% of the labor force is no longer required.&lt;/p&gt;

&lt;p&gt;At the very least, we’d need &lt;a href="https://en.wikipedia.org/wiki/Universal_basic_income"&gt;UBI&lt;/a&gt;, or taken to an extreme, &lt;a href="https://en.wikipedia.org/wiki/Fully_Automated_Luxury_Communism"&gt;FALC&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;This of course implies that every business, everywhere, would hop on this technology immediately. While most companies are always looking to maximize profits and increase efficiency, a lot of the business world turns at a slower pace than the technology realm likes to believe.&lt;/p&gt;

&lt;p&gt;I know teams that are just &lt;em&gt;now&lt;/em&gt; picking up tools like Docker, or working with frameworks like React. The enterprise world moves at a &lt;em&gt;very&lt;/em&gt; slow pace, and even if a tool was provided that could perform 90% of a team’s job developing new products, maintaining legacy software and complex inter-dependent systems will still need to be done during, or even after, an adaptation of an AI system.&lt;/p&gt;

&lt;p&gt;The software engineering profession has some ~100k open positions in the US alone, even if complete automation was made available today, there would be a large amount of time before full adaptation was reached.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This piece was mostly written as a way of getting a bunch of different thoughts I’ve had about AI and the programming profession out of my brain. It’s not a secret that I’m a very anxious person, and I won’t lie and say that I’m not nervous about my future as a software engineer.&lt;/p&gt;

&lt;p&gt;However, I’m excited for the tools that are being created to increase my productivity and allow me to be a better programmer. I encourage anyone out there interested in this career, or just starting out, to keep learning and utilizing the tools and techniques available to you.&lt;/p&gt;

&lt;p&gt;I think that we’re going to see a lot of interesting advancements in both AI and the software development world over the next few years, and it’s an exciting time to be in the field. I also believe that given the current trejectory, this profession &lt;strong&gt;is not&lt;/strong&gt; a dead end.&lt;/p&gt;

&lt;p&gt;Developers may become a lot more efficient over time, leading to smaller team sizes and available company roles. However, startups and smaller enterprises can utilize the same technology and low barrier of entry to launch new products and services that before, would have taken more time and effort.&lt;/p&gt;

&lt;p&gt;If I’m completely wrong about the above and programming is obsolete in a few years, well then it’s been a fun ride and you can catch me in a cabin in the woods gardening and making cabinetry.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Intercept and inspect http requests with reqon</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sun, 18 Sep 2022 06:01:35 +0000</pubDate>
      <link>https://forem.com/aschmelyun/intercept-and-inspect-http-requests-with-reqon-24h4</link>
      <guid>https://forem.com/aschmelyun/intercept-and-inspect-http-requests-with-reqon-24h4</guid>
      <description>&lt;p&gt;A few weeks ago I had an itch to scratch, I was working with a legacy application that was sending out a ton of curl requests to a production service. The problem was, &lt;em&gt;I had no idea what was in them&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Sure, I could have dug through the spaghetti and documented each one, but I needed an answer pretty quickly and instead just started &lt;code&gt;var_dump&lt;/code&gt;ing the requests to local text files. I thought, "there's gotta be a better way than this", and figured if I could just change the external API endpoint to something local, I could capture the requests and check them out in real-time.&lt;/p&gt;

&lt;p&gt;After searching for a while I found a few paid and hosted services that offer what I wanted, but nothing that was local or open source. So, I decided to make it! What I came up with, is &lt;strong&gt;reqon&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eoPwO0Hi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/aschmelyun/reqon/main/art/og_image.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eoPwO0Hi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/aschmelyun/reqon/main/art/og_image.png" alt="Screenshot of reqon running in a terminal window" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;You'll need to have npm installed and a node version of 16.0.0 or higher. Then, in your terminal just run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; reqon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Run the &lt;code&gt;reqon&lt;/code&gt; command in your terminal to start listening for requests on a local server. There's a few different options you adjust to your liking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;reqon [options]

options:
  --port=&amp;lt;port&amp;gt;             sets the port to listen for incoming requests
  --dashboard-port=&amp;lt;port&amp;gt;   sets the port the dashboard is available on
  --save-max=&amp;lt;number&amp;gt;       changes the max number of entries saved locally
  --save-file=&amp;lt;path&amp;gt;        changes the filepath used for local db, json ext required
  --no-dashboard            disables the dashboard, --dashboard-port is ignored
  --no-save                 disables saving locally, --save-file + --save-max ignored
  --help                    what you're seeing right now :)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of the options above have defaults associated with them, they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;port&lt;/strong&gt; default is &lt;code&gt;8080&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dashboard-port&lt;/strong&gt; default is &lt;code&gt;8081&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;save-max&lt;/strong&gt; default is &lt;code&gt;50&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;save-file&lt;/strong&gt; default is &lt;code&gt;~/.reqon/db.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Viewing requests
&lt;/h2&gt;

&lt;p&gt;Whenever a request is made to the listening endpoint, it's recorded and displayed immediately in the terminal. The details include the full route, http method, headers, url query variables, and request body.&lt;/p&gt;

&lt;p&gt;If you'd prefer something a bit more... easy on the eyes, a dashboard server is also spun up by default. It can be visited to see all of your current and past saved requests, along with their details, in a simplistic but effective layout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4VwsG0xC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iukiwlpekei8dcsiygui.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4VwsG0xC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iukiwlpekei8dcsiygui.png" alt="A screenshot of the reqon dashboard" width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By default, requests are stored locally in a JSON file with the help of &lt;a href="https://github.com/typicode/lowdb"&gt;LowDB&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Thanks for checking this project out!&lt;/p&gt;

&lt;p&gt;If you end up using it and have any suggestions or issues you'd like to discuss, please feel free to let me know. You can put in a request directly on the &lt;a href="https://github.com/aschmelyun/reqon"&gt;GitHub repo&lt;/a&gt;, or reach out to me on &lt;a href="https://twitter.com/aschmelyun"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>tools</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The difference between Traits, Interfaces, and Abstract Classes in PHP</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sun, 07 Aug 2022 05:53:45 +0000</pubDate>
      <link>https://forem.com/aschmelyun/the-difference-between-traits-interfaces-and-abstract-classes-in-php-55od</link>
      <guid>https://forem.com/aschmelyun/the-difference-between-traits-interfaces-and-abstract-classes-in-php-55od</guid>
      <description>&lt;p&gt;If you've been working with PHP regularly, chances are you've run across an Interface, Trait, or Abstract Class. At first glance, they might appear to have a few similarities between them, and it can be hard to make out their differences and use cases. By the end of this article, you should be able to easily tell what sets them apart, and when it's best to use one over the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  tl;dr
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;Abstract Class&lt;/strong&gt; can contain method signatures &lt;em&gt;as well as&lt;/em&gt; common methods, but can't be instantiated on its own. Good for creating a common parent to share between classes.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;Trait&lt;/strong&gt; is a group of properties and methods for &lt;em&gt;code re-use&lt;/em&gt;, and multiple can be added to a single class. Good for organization and reducing repetition. &lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Interface&lt;/strong&gt; is a set of method &lt;em&gt;signatures&lt;/em&gt; to enforce a particular implementation in the class they're added to. Good for adding structure and standardization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to learn more about each of these? &lt;strong&gt;Keep reading!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract Class
&lt;/h2&gt;

&lt;p&gt;An abstract class is structured a lot like a normal class, but contains abstract methods as well. Unlike a normal class though, it can't be instantiated by itself. They make good starting points for classes that would likely share a common parent.&lt;/p&gt;

&lt;p&gt;Let's say that we had Cat, Dog, and Hamster classes. These might share some common methods and functionality, so a parent abstract class could be created to both &lt;em&gt;add shared methods&lt;/em&gt; as well as &lt;em&gt;enforce required implementations&lt;/em&gt; in the child classes.&lt;/p&gt;

&lt;p&gt;A parent abstract class for those might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pet&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;hasFur&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have an abstract method &lt;code&gt;greet()&lt;/code&gt; which will force any class extending &lt;code&gt;Pet&lt;/code&gt; to implement that method. Then we have a public &lt;code&gt;hasFur()&lt;/code&gt; method which will be accessible to any object created from a class that extends this abstract class.&lt;/p&gt;

&lt;p&gt;So now we can create a class for a particular kind of pet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Pet&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'Meow!'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$cat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Meow!&lt;/span&gt;
&lt;span class="nv"&gt;$cat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasFur&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some takeaways about abstract classes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can't be instantiated on their own&lt;/li&gt;
&lt;li&gt;Abstract methods just declare a signature (no functionality)&lt;/li&gt;
&lt;li&gt;Used by a child class with &lt;code&gt;extends&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Act as a sort of partially-built class&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Trait
&lt;/h2&gt;

&lt;p&gt;Traits are a way to re-use code in multiple (and sometimes completely unrelated) classes. Unlike abstract classes, multiple traits can be used on a single class with the &lt;code&gt;use&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;They're regularly reached for to group together somewhat related methods and properties, adding that functionality to classes they're used in.&lt;/p&gt;

&lt;p&gt;For example, we might have two traits &lt;code&gt;HasLegs&lt;/code&gt; and &lt;code&gt;HasFins&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;HasLegs&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$steps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$steps&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;trait&lt;/span&gt; &lt;span class="nc"&gt;HasFins&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$laps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$laps&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're continuing with the animal examples from earlier, so let's say that we had a Cat object that clearly has legs and can walk. We can use our &lt;code&gt;HasLegs&lt;/code&gt; trait to give our &lt;code&gt;Cat&lt;/code&gt; class a &lt;code&gt;walk()&lt;/code&gt; method that can be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasLegs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$cat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$cat&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, what if we had a Duck object, that kind of has both legs and fins? We can use both traits and give our class &lt;code&gt;walk()&lt;/code&gt; and &lt;code&gt;swim()&lt;/code&gt; methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;HasLegs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HasFins&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;$duck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Duck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;walk&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;//or&lt;/span&gt;
&lt;span class="nv"&gt;$duck&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;swim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some takeaways about traits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect for code reuse&lt;/li&gt;
&lt;li&gt;Implemented in the class body with &lt;code&gt;use&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Multiple can be used in the same class&lt;/li&gt;
&lt;li&gt;Can have both properties and methods&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interface
&lt;/h2&gt;

&lt;p&gt;Interfaces are a way to enforce specific implementations in classes that use them. Interfaces can &lt;strong&gt;only&lt;/strong&gt; contain method signatures, there's zero functionality in them at all. They're mostly there to ensure structure and act as blueprints for building classes.&lt;/p&gt;

&lt;p&gt;Let's take this example of an interface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Bug&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;legs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;eyes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, there's no implementation of the &lt;code&gt;legs()&lt;/code&gt; and &lt;code&gt;eyes()&lt;/code&gt; methods, just the signature that they're required in classes that include this interface. So, if we create classes that implement our &lt;code&gt;Bug&lt;/code&gt; interface, those methods need to be available in each of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Spider&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Bug&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;legs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;eyes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Beetle&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Bug&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;legs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;eyes&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now we have two classes each implementing our &lt;code&gt;Bug&lt;/code&gt; interface, which are both required to include the &lt;code&gt;legs()&lt;/code&gt; and &lt;code&gt;eyes()&lt;/code&gt; methods as well as their own (usually unique) implementation. &lt;/p&gt;

&lt;p&gt;Some takeaways about interfaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Act as blueprints for classes&lt;/li&gt;
&lt;li&gt;Used in the class declaration with &lt;code&gt;implements&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Multiple can be used in the same class&lt;/li&gt;
&lt;li&gt;Can only contain method signatures (no properties)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;Hopefully now you have a better understanding of the distinctions and similarities between Abstract Classes, Traits, and Interfaces. Each of them are powerful and when combined in different ways can add a lot of power and structure to your PHP applications.&lt;/p&gt;

&lt;p&gt;If you have any questions about this, or anything else related to web development, feel free to let me know in the comments or reach out to me on &lt;a href="https://twitter.com/aschmelyun"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
    </item>
    <item>
      <title>When to use ref vs reactive in Vue</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sat, 16 Jul 2022 23:58:15 +0000</pubDate>
      <link>https://forem.com/aschmelyun/when-to-use-ref-vs-reactive-in-vue-24el</link>
      <guid>https://forem.com/aschmelyun/when-to-use-ref-vs-reactive-in-vue-24el</guid>
      <description>&lt;p&gt;The release of Vue 3 introduced two new ways of adding reactivity to data in your components: &lt;code&gt;ref()&lt;/code&gt; and &lt;code&gt;reactive()&lt;/code&gt;. There's been a bit of confusion surrounding which one's better, or when either should be used. I'm here to hopefully shed some light on their differences, and show how &lt;em&gt;I&lt;/em&gt; use them in my applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ref
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;ref()&lt;/code&gt; method takes a single value, and returns back a mutable and reactive object. Let's take a look at this example code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we wanted to create a method that incremented the amount up by one, you might be inclined to think we can do something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;increaseAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with &lt;code&gt;ref()&lt;/code&gt; you need to use an intermediary property called &lt;code&gt;value&lt;/code&gt; in order to &lt;em&gt;retrieve&lt;/em&gt; or &lt;em&gt;manipulate&lt;/em&gt; the data inside of the ref object. So instead, you'd do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;increaseAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ref()&lt;/code&gt; method takes any JavaScript primitive, so you can pass in booleans, strings, integers, or objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;active&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, world!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;amount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;andrew@test.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;andrew@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- Hello, world! --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When referencing or changing a value (outside of a template), you always have to use the &lt;code&gt;.value&lt;/code&gt; property on the returned object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reactive
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;reactive()&lt;/code&gt; method works similarly to ref, but it &lt;strong&gt;only accepts objects&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// not reactive&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// reactive&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike ref, we don't need to use an intermediary property like &lt;code&gt;.value&lt;/code&gt; in order to get or change the properties of our reactive object. We can just call the properties of the object directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt; &lt;span class="na"&gt;setup&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ashley&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;My name is &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt; &lt;span class="c"&gt;&amp;lt;!-- My name is Ashley --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An interesting feature of &lt;code&gt;reactive()&lt;/code&gt; is that it can also unwrap ref objects for use within itself.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 11&lt;/span&gt;

&lt;span class="nx"&gt;cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reactivity between the two also remains, so that updating the value of one triggers an update on the value of the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bottom line
&lt;/h2&gt;

&lt;p&gt;Both &lt;code&gt;ref()&lt;/code&gt; and &lt;code&gt;reactive()&lt;/code&gt; add reactivity to your Vue components. They allow you to have data that updates and responds in real-time across your application. The differences boil down to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What data you're passing in, and &lt;/li&gt;
&lt;li&gt;If you want to deal with an intermediary property to get the value&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For me personally, I usually stick with &lt;code&gt;ref()&lt;/code&gt; for reactive attributes in my components. If I start having more than just a few of them though, I create a local "state" object and use &lt;code&gt;reactive()&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;That way instead of this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;games&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Factorio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CS:GO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cities: Skylines&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;elem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Andrew&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;games&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Factorio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;CS:GO&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cities: Skylines&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;elem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#active&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Well, I hope this made the differences (and similarities) between ref and reactive in Vue a little clearer.&lt;/p&gt;

&lt;p&gt;If you have any questions about this, or anything else related to web development, feel free to let me know in the comments or reach out to me on &lt;a href="https://twitter.com/aschmelyun"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>vue</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 practical web dev projects that aren't todo lists</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sat, 21 May 2022 08:51:59 +0000</pubDate>
      <link>https://forem.com/aschmelyun/5-practical-web-dev-projects-that-arent-todo-lists-3n79</link>
      <guid>https://forem.com/aschmelyun/5-practical-web-dev-projects-that-arent-todo-lists-3n79</guid>
      <description>&lt;p&gt;I first learned web development by finding interesting sites around the web and trying to create them for myself. This of course isn't everyone's style of learning, but if you're one of those who thrive on &lt;em&gt;doing&lt;/em&gt; to move forward, keep on reading!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usually when I see examples pop up of beginner projects, they're pretty much the same things: todo lists, text editors, forms, etc.&lt;/strong&gt; These aren't necessarily &lt;em&gt;bad&lt;/em&gt; but they can get a little boring, especially when you start moving up your skillset and want to try to expand your knowledge with more complex projects.&lt;/p&gt;

&lt;p&gt;So I put together this little list of ones that I think might be good to try your hand at. They go up in complexity as you go down the list, so just keep that in mind. I've given each one a list of terms you can search to help build them, as well as programming languages or frameworks that might be good to use.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While a project might seem daunting, all of these are made up of smaller parts, so it can be easy to break them down into manageable chunks!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alright, here they are.&lt;/p&gt;




&lt;h2&gt;
  
  
  Image slideshow
&lt;/h2&gt;

&lt;p&gt;This will mostly test your frontend abilities, and there's a few ways you can build a slideshow of pictures that rotates through each one. You might be able to try to do something with pure CSS, but adding a bit of JavaScript to the mix will really hone the effect and overall user experience.&lt;/p&gt;

&lt;p&gt;Search these terms to help with the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS transitions&lt;/li&gt;
&lt;li&gt;Animating HTML elements&lt;/li&gt;
&lt;li&gt;Click events in JavaScript and how to handle them&lt;/li&gt;
&lt;li&gt;Image resizing with CSS&lt;/li&gt;
&lt;li&gt;Keyboard navigation and accessibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some languages and technologies you can use to build this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTML&lt;/li&gt;
&lt;li&gt;CSS&lt;/li&gt;
&lt;li&gt;JavaScript&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Markdown previewer
&lt;/h2&gt;

&lt;p&gt;Either with a website or with a command-line application, try creating a way to preview markdown files and convert them to HTML a browser can understand. I write a lot of notes in markdown, but seeing them nicely formatted helps with readability.&lt;/p&gt;

&lt;p&gt;Search these terms to help with the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parsing and using command-line arguments&lt;/li&gt;
&lt;li&gt;Leveraging third-party libraries&lt;/li&gt;
&lt;li&gt;Handling file input&lt;/li&gt;
&lt;li&gt;Outputting and styling pre-formatted HTML&lt;/li&gt;
&lt;li&gt;Building and distributing a compiled executable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some languages and technologies you can use to build this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Node (JavaScript)&lt;/li&gt;
&lt;li&gt;PHP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Weather app
&lt;/h2&gt;

&lt;p&gt;This is great because you get to play around with some real-life data. Try creating a website or a mobile application that takes in weather information from your area, and displays it in a nicely formatted way. You can be super complex and show every single data point, or go minimal with just the temperature and basic conditions. &lt;/p&gt;

&lt;p&gt;Search these terms to help with the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ingesting and using an external API&lt;/li&gt;
&lt;li&gt;Clean layout design and styling&lt;/li&gt;
&lt;li&gt;Live updates without having to refresh&lt;/li&gt;
&lt;li&gt;Styles changing based on current data&lt;/li&gt;
&lt;li&gt;Handling unexpected errors from an API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some languages and technologies you can use to build this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue (JavaScript)&lt;/li&gt;
&lt;li&gt;React Native (JavaScript compiled to mobile code)&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Twitter clone
&lt;/h2&gt;

&lt;p&gt;This one you can make as complicated or as simple as you'd like, but it will definitely bring you across both the frontend and backend stacks. At its heart, this project should let someone sign up for an account, create a username, and submit small messages that can be seen on their main feed page.&lt;/p&gt;

&lt;p&gt;Feel free to add additional complexity to it in the form of notifications, likes, blocked accounts, reblogs, user settings, etc.&lt;/p&gt;

&lt;p&gt;Search these terms to help with the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database design + relationships (one post -&amp;gt; many comments)&lt;/li&gt;
&lt;li&gt;Sending notifications&lt;/li&gt;
&lt;li&gt;Input sanitization and form submissions&lt;/li&gt;
&lt;li&gt;Infinite scrolling&lt;/li&gt;
&lt;li&gt;Account authentication and security&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some languages and technologies you can use to build this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Laravel + Vue (PHP + JavaScript)&lt;/li&gt;
&lt;li&gt;Django + React (Python + JavaScript)&lt;/li&gt;
&lt;li&gt;Kotlin + Svelte (Java + JavaScript)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stock market watcher
&lt;/h2&gt;

&lt;p&gt;This takes complexity up another notch by running code in the background, not just while someone is on your site. As a user of this project, I'd want to be able to track specific stock tickers and see their current price on the market.&lt;/p&gt;

&lt;p&gt;I'd also want to be notified whenever a price drops below, or goes up to, a certain amount. This will require you to run code in the background of your website to constantly refresh and compare the data coming in from the stock broker.&lt;/p&gt;

&lt;p&gt;Search these terms to help with the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Background (cron) jobs to watch stock prices&lt;/li&gt;
&lt;li&gt;Ingesting and displaying lots of data from external APIs&lt;/li&gt;
&lt;li&gt;User authentication and storing personal settings&lt;/li&gt;
&lt;li&gt;Formatting and sending email and text notifications&lt;/li&gt;
&lt;li&gt;Intricate graphing with or without third-party libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some languages and technologies you can use to build this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rails + Hotwire (Ruby)&lt;/li&gt;
&lt;li&gt;ASP.NET + Blazor (C#)&lt;/li&gt;
&lt;li&gt;Laravel + LiveWire (PHP)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope some of these projects have inspired you to look in more uncommon directions for ways to help you hone your programming skills. To this day I'm still building things in my free time in order to learn new frameworks, languages, or just to see if I can actually do it.&lt;/p&gt;

&lt;p&gt;If you have any questions about this, or anything else related to web dev, feel free to let me know in the comments or reach out to me on &lt;a href="https://twitter.com/aschmelyun"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I built a receipt printer for GitHub issues</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Fri, 25 Mar 2022 07:15:12 +0000</pubDate>
      <link>https://forem.com/aschmelyun/i-built-a-receipt-printer-for-github-issues-51b7</link>
      <guid>https://forem.com/aschmelyun/i-built-a-receipt-printer-for-github-issues-51b7</guid>
      <description>&lt;p&gt;I have a lot of &lt;a href="https://github.com/aschmelyun/repos"&gt;side projects&lt;/a&gt; on GitHub. Some of them are kind of popular, and I tend to get issues posted from time to time. The problem though is that usually they kind of get lost in the mix of my emails, or I forget to go through my repos and add new items to my todo list.&lt;/p&gt;

&lt;p&gt;I've been occasionally writing new issues down on sticky notes whenever I see a notification for an issue, but I always wanted an excuse to streamline the process a bit more. After seeing a receipt printer spitting out orders while grabbing some take-out the other day, I wondered if I could use one to print out a ticket each time an issue was added to one of my repos.&lt;/p&gt;

&lt;p&gt;Spoiler alert, it worked!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1506960015063625733-277" src="https://platform.twitter.com/embed/Tweet.html?id=1506960015063625733"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1506960015063625733-277');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1506960015063625733&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;So let's dive in and I'll show you exactly what I used, and how I set it up!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardware list
&lt;/h2&gt;

&lt;p&gt;In order to get started, I'll need a thermal receipt printer and some way to get data into it. I ended up using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Epson TM-T88IV&lt;/li&gt;
&lt;li&gt;Raspberry Pi Zero W&lt;/li&gt;
&lt;li&gt;Micro USB to USB adapter&lt;/li&gt;
&lt;li&gt;USB Type-B cable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason that I went with an Epson thermal printer is that they use the ESC/POS command set, for which there's &lt;a href="https://github.com/search?q=esc%2Fpos"&gt;established libraries&lt;/a&gt; in a variety of programming languages. Plus they're pretty ubiquitous in the second-hand market, and I was able to pick one up on Ebay along with some receipt paper for a pretty fair price.&lt;/p&gt;

&lt;p&gt;The other piece I need is some kind of hardware to connect from the internet to the printer, and facilitate the actual data sending. I could just hook it up to my PC, but I want this to be a fully-contained unit that could just be constantly on idle sitting in a corner. I have an old &lt;a href="https://www.raspberrypi.com/products/raspberry-pi-zero-w/"&gt;Raspberry Pi Zero W&lt;/a&gt; laying around that I'm not using, so I'll choose that. &lt;/p&gt;

&lt;p&gt;Because the RPi Zero has just a single micro USB port, I'll use an adapter as well as a USB Type-B cable to connect it to the receipt printer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sending data to the printer
&lt;/h2&gt;

&lt;p&gt;Alright, so we have the printer hooked up, the Raspberry Pi good to go, but now I need a way to send data &lt;em&gt;to&lt;/em&gt; the printer &lt;em&gt;from&lt;/em&gt; the Raspberry Pi. This could easily be accomplished with Node or Python, but since I'm a PHP developer and I enjoy stretching the limitations of the language, I'll reach for that. Luckily for me, there's a &lt;a href="https://github.com/mike42/escpos-php"&gt;pretty solid library&lt;/a&gt; for working with ESC/POS commands available in PHP.&lt;/p&gt;

&lt;p&gt;Before I write any code though, I have to make sure the printer is available to the program I create. Since I'm using Ubuntu on the Raspberry Pi, I should be able to access it via &lt;code&gt;/dev/usb/lp0&lt;/code&gt; (or another lp#). But it might require a little bit of prep work first.&lt;/p&gt;

&lt;p&gt;First, I'll open up a terminal in the device that my printer is connected to (for me, that's the Raspberry Pi). I'll run the command &lt;code&gt;lsusb&lt;/code&gt; to get the &lt;em&gt;Product ID&lt;/em&gt; and &lt;em&gt;Vendor ID&lt;/em&gt; from the connection to your printer. It returns something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Bus 002 Device 001: ID 04b2:0202 Epson TM-T888IV Device Details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I create a udev rule to let users belonging to the &lt;strong&gt;dialout&lt;/strong&gt; group use the printer. I create the file &lt;code&gt;/etc/udev/rules.d/99-escpos.rules&lt;/code&gt; and add the following to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SUBSYSTEM&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="s2"&gt;"usb"&lt;/span&gt;, ATTRS&lt;span class="o"&gt;{&lt;/span&gt;idVendor&lt;span class="o"&gt;}==&lt;/span&gt;&lt;span class="s2"&gt;"04b2"&lt;/span&gt;, ATTRS&lt;span class="o"&gt;{&lt;/span&gt;idProduct&lt;span class="o"&gt;}==&lt;/span&gt;&lt;span class="s2"&gt;"0202"&lt;/span&gt;, &lt;span class="nv"&gt;MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"0664"&lt;/span&gt;, &lt;span class="nv"&gt;GROUP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dialout"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Being sure to replace the hex values for the vendor and product ID's with what I got returned back from &lt;code&gt;lsusb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If my user(s) aren't part of the &lt;em&gt;dialout&lt;/em&gt; group, I try to add them to it now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; dialout pi &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; dialout root
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then finally, I have to restart udev:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service udev restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I have the connection ready, I can start writing some code to test this out. First, I'll require that library from earlier with Composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require mike42/escpos-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that's installed, I need to write some code to send data to the printer. I'll create a file called &lt;code&gt;index.php&lt;/code&gt;, and add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;require&lt;/span&gt; &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/vendor/autoload.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Mike42\Escpos\PrintConnectors\FilePrintConnector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Mike42\Escpos\Printer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$connector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FilePrintConnector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/dev/usb/lp0'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Printer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$connector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Hello, world!'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;feed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cut&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So now to run this, all I have to do is execute the script with PHP and root permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;php index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything worked out fine, &lt;strong&gt;Hello, world!&lt;/strong&gt; will have printed on a receipt, with two lines skipped, and then the receipt will have cut. How that all works is pretty straightforward.&lt;/p&gt;

&lt;p&gt;A print connector is created to the 'file' &lt;code&gt;/dev/usb/lp0&lt;/code&gt;, which is the usb adapter that the printer is connected to. The printer commands that are subsequently used (&lt;code&gt;text()&lt;/code&gt;, &lt;code&gt;feed()&lt;/code&gt;, &lt;code&gt;cut()&lt;/code&gt;), stream the raw commands associated with those actions to the printer through that connection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you get an error about permissions when sending to &lt;code&gt;/dev/usb/lp0&lt;/code&gt; or something similiar to that, try running &lt;code&gt;sudo chmod +777 /dev/usb/lp0&lt;/code&gt; and seeing if that fixes it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Using these methods, I can move on to connecting this with GitHub and populating the receipts with some actual data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connecting to GitHub
&lt;/h2&gt;

&lt;p&gt;GitHub makes it easy to listen to events on repos with &lt;a href="https://docs.github.com/en/developers/webhooks-and-events/webhooks/about-webhooks"&gt;webhooks&lt;/a&gt;. By going to one of my repo's settings page and navigating to the webhooks section, I can create a hook that will POST to a specific URL on a given action. For my case, I want to print out a ticket when a new issue is created, so I choose just the 'Issues' section. I also set the data type as JSON, since that's what I enjoy working with.&lt;/p&gt;

&lt;p&gt;But before I continue, I need to have a URL that GitHub could &lt;em&gt;send that POST request to&lt;/em&gt;. First, I ssh back into the Raspberry Pi and start up the local PHP server by using the -S flag in my project's directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;php &lt;span class="nt"&gt;-S&lt;/span&gt; 127.0.0.1:8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that it's running, I need a way to access that port on my Raspberry Pi, while it's on my local network. I don't really want to expose my home's IP address or worry about creating a pass through my router. So, I just ended up using &lt;a href="https://ngrok.com/"&gt;ngrok&lt;/a&gt; to tunnel through to the exposed port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ngrok http 8000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that loads up, I copy the provided https url, and paste it in the GitHub webhook url field. Everything looks good, and I save the webhook. As soon as I save, there should be a test request that's sent out, ngrok accepts the request, tunnels it to the local PHP server, and another &lt;strong&gt;Hello, world!&lt;/strong&gt; will print out.&lt;/p&gt;

&lt;p&gt;Now I'm ready to actually use the incoming request from GitHub to build out a ticket.&lt;/p&gt;

&lt;h2&gt;
  
  
  The final code
&lt;/h2&gt;

&lt;p&gt;Now I'll make some modifications to my code from earlier. First, I should discard anything that's not a POST request. So before initializing the FilePrintConnection, I add these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_METHOD'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s1"&gt;'POST'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'Error: Expecting POST request'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after the FilePrintConnection and Printer initialization, I'll decode the entire JSON request from GitHub as an associative array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;json_decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file_get_contents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'php://input'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I can use the printer methods from before and the data array from GitHub to build up the receipt I want! Using the Escpos library, formatting text requires a &lt;strong&gt;lot&lt;/strong&gt; of repetitive code. For a small example, here's what a bold and underlined title of the issue, along with the plain text body, looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setUnderline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// start underlined text&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setEmphasis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// start bolded text&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'issue'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'title'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setEmphasis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// stop bolded text&lt;/span&gt;
&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setUnderline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// stop underlined text&lt;/span&gt;

&lt;span class="nv"&gt;$printer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'issue'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'body'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you'd like to see the entire code that I used to format my ticket in the tweet above, you can check it out on the &lt;a href="https://github.com/aschmelyun/github-receipts"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now to test it out, all I have to do is go to the repository where I set up my webhook, create a new issue, and wait for the printer to deliver a ticket :magic:.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up and next steps
&lt;/h2&gt;

&lt;p&gt;Alright, so where to go from here? This is definitely a simple proof of concept, but we can &lt;strong&gt;expand on it&lt;/strong&gt; a few different ways.&lt;/p&gt;

&lt;p&gt;For the tickets themselves, a QR code could be added to link directly to the issue on GitHub. You could also add in more details from the issue itself like tags and severity.&lt;/p&gt;

&lt;p&gt;You could also use this concept to handle basically any data coming from a webhook or through an API request. Like printing tickets from apps like Jira or Bugsnag, exceptions thrown from production applications, or even daily todo items and grocery lists!&lt;/p&gt;

&lt;p&gt;So, what do you think? If you have any ideas for how you'd improve on this setup, or just have a question or comment, please let me know in the discussion below or on my &lt;a href="https://twitter.com/aschmelyun"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>php</category>
      <category>iot</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Storing time-series data in InfluxDB with Laravel</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sun, 20 Mar 2022 02:09:59 +0000</pubDate>
      <link>https://forem.com/aschmelyun/storing-time-series-data-in-influxdb-with-laravel-2he4</link>
      <guid>https://forem.com/aschmelyun/storing-time-series-data-in-influxdb-with-laravel-2he4</guid>
      <description>&lt;p&gt;I've been running a &lt;a href="https://aschmelyun.com/blog/building-an-interactive-raspberry-pi-dashboard-with-laravel-grafana-and-docker/"&gt;Laravel application on a Raspberry Pi&lt;/a&gt; that handles the bulk of my home automation and monitoring, like keeping track of temperature and humidity from sensors around my house. Originally I was storing this all on a MySQL database, and yeah that worked good enough. But, after 2 years of continuous data it was starting to get a little bulky.&lt;/p&gt;

&lt;p&gt;I figured, if I'm storing time-series data, it makes sense to use a popular &lt;a href="https://github.com/influxdata/influxdb"&gt;open-source solution&lt;/a&gt; that's optimized exactly for that. In this article, I'm going to show you how I tied in the &lt;strong&gt;time-series database InfluxDB into my existing Laravel application&lt;/strong&gt;. After doing this, not only have my response times improved drastically, but the database size has shrunk considerably as well.&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Notes
&lt;/h2&gt;

&lt;p&gt;In this article, I'll assume you have the following already set up and ready to go:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Laravel 8+ application&lt;/li&gt;
&lt;li&gt;An InfluxDB V2 database&lt;/li&gt;
&lt;li&gt;A bucket (database) created in InfluxDB for your data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won't be going over &lt;em&gt;how&lt;/em&gt; to get an InfluxDB instance set up as there's a few different ways that this can be accomplished. If you'd like me to dive in deeper in a separate article about how I got it working (in this particular case using Docker and Docker Compose), feel free to let me know either in the comments or on Twitter!&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Alright, the first thing that we need to do is install the InfluxDB PHP client so that we can use it to connect to our database through our Laravel application. There's something important to note here though.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;There are two completely different libraries depending on the version of InfluxDB that you're using.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm using InfluxDB version 2 as stated in the prerequisites list above, so &lt;a href="https://github.com/influxdata/influxdb-client-php"&gt;this is the library&lt;/a&gt; I'm going to install. There are pretty significant syntax differences, and if you follow this tutorial trying to connect to an older V1 instance, you'll probably run into problems down the line.&lt;/p&gt;

&lt;p&gt;So, in our project root, we can install that dependency like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require influxdata/influxdb-client-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in our Laravel application's &lt;code&gt;.env&lt;/code&gt; file, we'll add in a few new values to handle connecting to the InfluxDB instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INFLUXDB_HOST=
INFLUXDB_TOKEN=
INFLUXDB_BUCKET=
INFLUXDB_ORG=
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;INFLUXDB_HOST&lt;/strong&gt;: This is the hostname and port that your instance resides on. If you're self-hosting, it's likely that this will be &lt;code&gt;127.0.0.1:8086&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INFLUXDB_TOKEN&lt;/strong&gt;: The access token. This can be set on creation of the InfluxDB instance, or automatically generated. It's found in the InfluxDB dashboard under the &lt;em&gt;Data &amp;gt; API Tokens&lt;/em&gt; section.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INFLUX_BUCKET&lt;/strong&gt;: A bucket in InfluxDB is basically a database, and this should correspond to the name of the one you created to store your data in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INFLUXDB_ORG&lt;/strong&gt;: The organization name for your InfluxDB instance, usually specified during setup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have all of these added to your &lt;code&gt;.env&lt;/code&gt; file, we can move on to adding in code to test our connection out. Let's create a route, controller, and method to handle an incoming data point that needs to be saved in our bucket.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;routes/web.php&lt;/code&gt; file, it could look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/temperature'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;App\Http\Controllers\TemperatureController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'store'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then that controller class, &lt;code&gt;TemperatureController.php&lt;/code&gt;, would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TemperatureController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// store our data point in InfluxDB&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alright, now we're ready to start writing code to actually use our database!&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up The Client
&lt;/h2&gt;

&lt;p&gt;First, we need to instantiate a Client class from the InfluxDB PHP library that we installed earlier.&lt;/p&gt;

&lt;p&gt;Because this is version 2, the main namespace is &lt;code&gt;IndexDB2&lt;/code&gt;. The client constructor requires an array to be passed in, containing the data necessary to connect to the instance and determine the precision of the bucket. That looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;IndexDB2\Client&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_HOST'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'bucket'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_BUCKET'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'org'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_ORG'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'precision'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;InfluxDB2\Model\WritePrecision&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first four attributes are pretty obvious, they're the values that we added into our &lt;code&gt;.env&lt;/code&gt; file earlier, and we're using the &lt;code&gt;env()&lt;/code&gt; helper to pull those values out. The last one however, is new.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Precision&lt;/em&gt; determines what the format and precision of the timestamps associated with data in your bucket will be. There's four possible values that we can use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;s&lt;/strong&gt;: Seconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ms&lt;/strong&gt;: Milliseconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;us&lt;/strong&gt;: Microseconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ns&lt;/strong&gt;: Nanoseconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;WritePrecision&lt;/code&gt; class is merely a helper enum that returns back one of those four possible values. I'm writing data once every 2 minutes, so precision above seconds isn't &lt;em&gt;really&lt;/em&gt; necessary to me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Data
&lt;/h2&gt;

&lt;p&gt;After the client has been initialized, we have to call a method on it to create something called a Write API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$writeApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createWriteApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;writeApi&lt;/code&gt; object exposes a few methods that we can use to &lt;strong&gt;write data to our bucket&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Before we do that, we first have to create a &lt;strong&gt;Point&lt;/strong&gt; object. Then using a chain of methods, set it up to be used by our Write API object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'temperature'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up a measurement datapoint called &lt;code&gt;temperature&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's say that I have a bucket called 'metrics', I can have multiple different attributes all inside of that one bucket (like temperature, humidity, light level, etc) and be able to query them individually or together as needed.&lt;/p&gt;

&lt;p&gt;The Point object then needs to have a value associated with it, so we use &lt;code&gt;addField&lt;/code&gt; to attach that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fahrenheit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;74.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we have multiple measurements with the same name that are in the bucket, we can differentiate &lt;em&gt;those&lt;/em&gt; by using tags to determine things like room placements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bedroom'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple tags can be used, but I just need the one.&lt;/p&gt;

&lt;p&gt;Finally, we need to set &lt;em&gt;when&lt;/em&gt; this measurement was taken by using the &lt;code&gt;time()&lt;/code&gt; method. Remember that I set my write precision to seconds, so I can just use the amount of epoch seconds.&lt;/p&gt;

&lt;p&gt;In PHP, we'll use the default &lt;code&gt;time()&lt;/code&gt; function for that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have our point all configured, we just write it to our bucket using the Write API object we created earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$writeApi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will return back &lt;code&gt;true&lt;/code&gt; if everything goes well, or throw an exception if there was something wrong along the way. After that, your data should now be available in your InfluxDB bucket!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Refactoring the above code a bit and putting it all together, our &lt;code&gt;TemperatureController&lt;/code&gt;'s entire store method looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nf"&gt;IndexDB2\Client&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'url'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_HOST'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'token'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'bucket'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_BUCKET'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'org'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INFLUXDB_ORG'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'precision'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;InfluxDB2\Model\WritePrecision&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;S&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$writeApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createWriteApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nv"&gt;$point&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Point&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;measurement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'temperature'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'location'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bedroom'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;addField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fahrenheit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;74.3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$writeApi&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nc"&gt;InfluxDB2\ApiException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after hitting that route endpoint with a request, we can go to our InfluxDB dashboard and see the datapoint that we added. In this case, a temperature reading of 74.3, tagged in bedroom. Now you're ready to easily ingest and query time-series data in your Laravel application with the help of InfluxDB!&lt;/p&gt;

&lt;p&gt;If you have any questions about this, web development in general, or would like to see more shorter-form content from me, feel free to follow me on Twitter &lt;a href="https://twitter.com/aschmelyun"&gt;@aschmelyun&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>iot</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Installing a local Composer package in your PHP project</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Sun, 23 Jan 2022 03:45:53 +0000</pubDate>
      <link>https://forem.com/aschmelyun/installing-a-local-composer-package-in-your-php-project-278k</link>
      <guid>https://forem.com/aschmelyun/installing-a-local-composer-package-in-your-php-project-278k</guid>
      <description>&lt;p&gt;If you've worked in a PHP project, chances are you've dealt with the &lt;a href="https://getcomposer.org"&gt;Composer&lt;/a&gt; package manager. As a full-stack developer, I think it's one of the better ones that I use on a regular basis, consistently improving while remaining &lt;em&gt;relatively&lt;/em&gt; simple.&lt;/p&gt;

&lt;p&gt;One of the more difficult things to do with it though, is &lt;strong&gt;adding a local package for use in a larger PHP project&lt;/strong&gt;. Whether you've downloaded a private source, or are developing a package locally, this method will work to get your package into Composer.&lt;/p&gt;

&lt;p&gt;First, open up your PHP project's &lt;code&gt;composer.json&lt;/code&gt; file. You're going to want to add a repositories array if one isn't already present. That array expects a list of package sources, and we're going to provide our local package's directory as one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"repositories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./packages/aschmelyun/my-package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"options"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"symlink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passing in the &lt;code&gt;"symlink": true&lt;/code&gt; option means that our package's source folder will be symlinked into the &lt;code&gt;vendor&lt;/code&gt; directory of our PHP project.&lt;/p&gt;

&lt;p&gt;You might be asking: &lt;strong&gt;Why don't we just add the source of our package right into the vendor directory?&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You're right, we &lt;em&gt;could&lt;/em&gt; do that and it &lt;em&gt;might&lt;/em&gt; work. But keeping everything contained in the Composer ecosystem ensures that our project dependencies run smoothly. It also opens up the door to use other features like multiple packages in the same source repo, or ensuring our local package meets dependency requirements.&lt;/p&gt;

&lt;p&gt;Okay, after that's done, we just need to update our &lt;code&gt;require&lt;/code&gt; list with our package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"require"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"aschmelyun/my-package"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@dev"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@dev&lt;/code&gt; version string ensures that no matter what, the source code that we have in our package folder &lt;em&gt;should&lt;/em&gt; be what gets added in and referenced in our PHP project.&lt;/p&gt;

&lt;p&gt;The package name that we use in the above should match the &lt;code&gt;name&lt;/code&gt; attribute in the &lt;code&gt;composer.json&lt;/code&gt; of the PHP package, &lt;strong&gt;not just the folder path&lt;/strong&gt;. For instance, here's what the &lt;code&gt;composer.json&lt;/code&gt; might look like for the above test package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"aschmelyun/my-package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"Just a test package"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"require"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"php"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"^7.2 || ^8.0"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s2"&gt;"autoload"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"psr-4"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="s2"&gt;"ASchmelyun&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;MyPackage&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"src/"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s2"&gt;"minimum-stability"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;"prefer-stable"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last two attributes in the above file aren't &lt;em&gt;usually&lt;/em&gt; required, but could help if you run into any errors getting your local package to load into Composer.&lt;/p&gt;

&lt;p&gt;Finally, all you have to do is update your Composer packages from the project root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✨ &lt;strong&gt;Ta-da!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now you can use your package in your PHP project just like you would if you installed it through &lt;code&gt;composer require ...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Have any questions about web development, or would like to see more shorter-form content from me? Follow me on Twitter &lt;a href="https://twitter.com/aschmelyun"&gt;@aschmelyun&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>php</category>
      <category>webdev</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Keyboard shortcuts to drastically increase your productivity</title>
      <dc:creator>Andrew Schmelyun</dc:creator>
      <pubDate>Mon, 17 Jan 2022 06:13:51 +0000</pubDate>
      <link>https://forem.com/aschmelyun/keyboard-shortcuts-to-drastically-increase-your-productivity-2157</link>
      <guid>https://forem.com/aschmelyun/keyboard-shortcuts-to-drastically-increase-your-productivity-2157</guid>
      <description>&lt;p&gt;Over the last three years, I've been adding to and maintaining a keyboard shortcuts website called &lt;a href="https://usethekeyboard.com"&gt;Use The Keyboard&lt;/a&gt;. Over that time I've amassed some &lt;strong&gt;5100 individual shortcuts&lt;/strong&gt; spread out across &lt;strong&gt;85 apps, programs, and websites&lt;/strong&gt;. In this article, I'm going to break down a select few from a variety of programs and sites that I use on a regular basis as a web developer (and occasional designer).&lt;/p&gt;

&lt;h2&gt;
  
  
  Notion
&lt;/h2&gt;

&lt;p&gt;A popular workspace app, Notion has its share of helpful keyboard shortcuts. Whenever I'm using it, I find myself constantly using one of these to make navigating through my blocks a lot easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + P&lt;/strong&gt; - Opens a quick-finder bar to search for any page in your workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + Enter&lt;/strong&gt; - Opens an active block as a separate page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + U&lt;/strong&gt; - Goes to the parent page of the page you're currently on&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Chrome DevTools
&lt;/h2&gt;

&lt;p&gt;Chrome's DevTools window is open pretty much any time I'm working on a site. Despite it being a part of a larger software, it comes with its own set of shortcuts to make navigating through different panels and toggling features a bit easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + M&lt;/strong&gt; - Toggles device mode, which emulates screens of different sizes for the current page&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + `&lt;/strong&gt; - Focuses on the console, and &lt;strong&gt;Ctrl + L&lt;/strong&gt; clears the console when you're active in it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + R&lt;/strong&gt; - Hard reloads the page, clearing the cache before-hand&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  GitHub
&lt;/h2&gt;

&lt;p&gt;GitHub has a &lt;em&gt;surprising&lt;/em&gt; amount of keyboard shortcuts on its website, and once you've mastered a few of them you'll wonder how you lived without them.&lt;/p&gt;

&lt;p&gt;There's shortcuts for navigating through respositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;G, then I&lt;/strong&gt; takes you to the Issues tab&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;G, then P&lt;/strong&gt; takes you to the pull requests tab&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;G, then C&lt;/strong&gt; takes you to the code tab&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And browsing through the source code of an app, there's a bunch of single-letter shortcuts that you can press on any page to activate certain features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;T&lt;/strong&gt; will bring up a prompt to search for a file across the whole repo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;W&lt;/strong&gt; lets you switch to a different branch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;B&lt;/strong&gt; opens up the blame view&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Figma
&lt;/h2&gt;

&lt;p&gt;Like most design programs, Figma relies heavily on the keyboard to improve speed and flow throughout the app. Here's a few lesser-known ones that I tend to reach for on a pretty regular basis.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + A&lt;/strong&gt; selects the inverse of what you're currently selecting&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Shift + V&lt;/strong&gt; while pasting text matches the style of what you copied&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + Alt + C&lt;/strong&gt; copies the &lt;em&gt;properties&lt;/em&gt; of what's currently selected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Visual Studio Code
&lt;/h2&gt;

&lt;p&gt;VSCode being a code editor (or even a full IDE depending on how you use it), definitely leans heavy on the keyboard. For this app alone I counted well over 100 individual shortcuts that you have available! I won't detail them all here, but I'll point out a few that I think are worth highlighting.&lt;/p&gt;

&lt;p&gt;When it comes to searching for or replacing text, you have a few useful commands.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Alt + Enter&lt;/strong&gt; selects all occurances of a find match&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + K, then Ctrl + D&lt;/strong&gt; moves the last selection to the next find match&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And when working on larger projects or more complicated codebases, I tend to reorganize my editor with one of these.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;*&lt;em&gt;Ctrl + \*&lt;/em&gt; splits the editor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + K, then Ctrl + left/right&lt;/strong&gt; switches the focus to the previous, or next editor group&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discord
&lt;/h2&gt;

&lt;p&gt;Despite being a (relatively) simple and straightforward chat app, Discord comes with a decent amount of keyboard shortcuts that make using it just a little more exciting. Any chance I can get to not take my hands off my keyboard while working on something is a plus. Now, I'm pretty sure my most-used shortcuts for this revolve around navigating the app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shift + PageUp&lt;/strong&gt; jumps to the &lt;em&gt;oldest&lt;/em&gt; unread message&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alt + Up/Down&lt;/strong&gt; navigates between channels, while &lt;strong&gt;Ctrl + Alt + Up/Down&lt;/strong&gt; navigates between servers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ctrl + K&lt;/strong&gt; finds or starts a private message&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  That's about it
&lt;/h2&gt;

&lt;p&gt;Well, that's about all I can think of for now! If you have a keyboard shortcut that you'd like to see added to the site, feel free to open up a &lt;a href="https://github.com/aschmelyun/use-the-keyboard"&gt;pull request on the repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have any questions about web development, or would like to see more shorter-form content from me, follow me on Twitter &lt;a href="https://twitter.com/aschmelyun"&gt;@aschmelyun&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>beginners</category>
      <category>keyboard</category>
    </item>
  </channel>
</rss>
