triplebatman

Editing and Bug Fixes

Small update to the blog, mostly just to force myself to do something with it to kick off the new year - I added an edit function! What does this mean for you, the readers? I'll let Kuni answer that one:

This is purely an internal change to help me if I ever need to edit a post. Generally, this hasn't been a problem. This blog isn't serious and I'm not going to freak out over the occasional typo, grammar mistake, etc. The only edits I've done in the past were to fix links that didn't have the proper markup to open in a new tab, fixing broken / incorrect image paths, and fixing egregious errors like text that got copy / pasted twice.

However, the few times I DID have to do those minor edits, it was a bit of a pain because I had to go into my admin console for the hosting service I use and manually update the values in the database. That sucks. So I coded up a couple little PHP pages to let me easily edit my posts from here on out. Both of them are locked behind an admin user and password, so I can't link you to them, but I'll provide some code snippets and screenshots.

The first of the two pages is dead simple - it fetches a list of posts and renders the post titles in a dropdown. You can select a post, click the "Edit" button, and it will take you to the actual edit page for that post:

This was some pretty simple PHP that just runs a MySQL SELECT statement to get the post titles and ids, and then a loop to render them as options in the dropdown list. Also, a simple JavaScript function to grab the id value from the dropdown and send you to the actual edit page for that post when you click the "Edit" button:

    // ... some boilerplate code

    <script type="application/javascript">
      function editPost() {
        window.location.href = `/blog/post/edit?id=${document.getElementById("posts").value}`;
        return;
      }
    </script>

    // ... even more boilerplate code

    $sql = 'SELECT id, title FROM posts ORDER BY id DESC';
    $result = mysqli_query($conn, $sql);
    $total_results = mysqli_num_rows($result);

    if ($total_results > 0) {
      echo '<label for="posts">Select Post to Edit:</label>'; 
      echo '<br/>';
      echo '<select name="posts" id="posts">';
      
      // store row data from request
      while($row = mysqli_fetch_assoc($result)) {
        echo '<option value="'.$row['id'].'">'.$row['title'].'</option>';
      }
      
      echo '</select>';
      echo '<br/>';
      echo '<br/>';
      echo '<button onclick="editPost()">Edit</button>';
    }

    // ... even more boilerplate code

The actual edit page just does some more MySQL selects with a given post id, renders the data into a form, and has some submit code that runs a MySQL update statement:

    // ... boilerplate

    $id = $_GET['id'];

    if (is_numeric($id)) {
      $post_sql = 'SELECT title, body, og_image_url, created_at FROM posts WHERE id='.$id;
      $result = mysqli_query($conn, $post_sql);

      // get tags
      $tag_sql = "SELECT name FROM tags WHERE id IN (SELECT DISTINCT(tag_id) FROM (SELECT * FROM posts LEFT JOIN posts_tags ON posts_tags.post_id = posts.id WHERE id = $id) as tags)";
      $tag_result = mysqli_query($conn, $tag_sql);

      $tags = [];
      while($tag_row = mysqli_fetch_assoc($tag_result)) {
        array_push($tags, $tag_row['name']);
      }

      if (mysqli_num_rows($result) > 0) {
        $row = mysqli_fetch_assoc($result);

        echo $row;

        $page_content = '<form action="/blog/post/edit/submit_post.php"  method="post">
          <input type="hidden" id="id" name="id" value="'.$id.'" />
          <label for="title">Post Title:</label>
          <br/>
          <input value="'.$row['title'].'" name="title" id="title" type="text" size="60" />
          <br/>
          <br/>
          <label for="body">Post Body:</label>
          <br/>
          <textarea name="body" id="body" cols="60" rows="30">'.$row['body'].'</textarea>
          <br/>
          <br/>
          <label for="tags">Tags (comma separated):</label>
          <br/>
          <input value="'.implode(",",$tags).'" name="tags" id="tags" size="60" />
          <br/>
          <br/>
          <label for="ogImageUrl">OG Image URL (full URL):</label>
          <br/>
          <input value="'.$row['og_image_url'].'" name="ogImageUrl" id="ogImageUrl" size="60" />
          <br/>
          <br/>
          <button type="submit">Submit</button>
        </form>';
      } else {
        echo '<h1>Invalid post id</h1>';
      }
    } else {
      echo '<h1>Invalid post id</h1>';
    }

    // ... more-lerplate

This gives us a simple form to edit our blog posts, which looks something like this:

The code in submit_post.php is pretty boring - just a MySQL update with the form values - so I won't bore you with that here. Just know that it works, and so now I can edit posts via a web interface easily, which makes it more likely for me to do more stuff with this blog in the future.

One last thing I found while working on this feature was a bug that already existed - when you submitted a list of empty tags when creating (and now, editing) a post, it would create an empty string tag and attach it to the post, results in posts that looked like this:

That was no good, but it took a bit of digging to figure out why it was happening, and the answer is that a certain PHP function does not behave the way I expect it to, and that function's name is explode. It takes in two arguments - a separator, and a string. It then splits up that string based on the separator, and returns the resulting set of substrings as an array. So, for example, passing in the separator "," and the string "a,b,c" to the explode function returns the array ["a","b","c"]. Simple, right? I was using this to pass in the list of tags as a comma-separated string when creating a new post, and then iterating over the array and inserting the tags as DB entries.

Now, I thought this would handle empty string by just returning an empty array. Unfortunately, that was a naive and incorrect assumption - it instead returns an array with a single entry, and that single entry is an empty string. So it was applying a tag of "" to all posts submitted with no tag array. So far, I haven't actually done this in my actual live blog, so it hasn't come up, but I was able to go ahead and fix the error by adding a simple ternary to check for string length before trying to call explode, which fixed the bug:

    $tags = strlen($_REQUEST["tags"]) ? explode(',', $_REQUEST["tags"]) : [];

And that's pretty much it! Hopefully I'll have some more interesting stuff to post about as the year goes on, but for now this quality of life improvement makes me feel better about writing posts and working more on the blog in the future.

See ya next time!

tags:programming