Archiving via Test0:54
Likely not, but you have to think about these things. So instead of deleting it permanently, I think a good way to go would be to simply mark it as archived. And if we scroll down, Bam Bam Pool had this very idea, and I agree with it. It would be the easiest to just flag a channel as archived with a column in the table and filter them out by default. I think that's a nice and simple way to go. So let's begin adding support for that. Let's start with a test. So if we go into, hmm, Tests, we're not doing a feature, we're going to do a ChannelTest
Let's start with a test. So if we go into, hmm, Tests, we're not doing a feature, we're going to do a ChannelTest for now, and at the moment, of course, it's passing. Let's say a Channel can be archived. So given we have a Channel, then the status of whether it is archived should be false by default. So we'll say assertFalse $channel->archived. Now at the moment, of course, that archived column doesn't exist, but let's give it a run. Yep, failed asserting that null is false.
Add Archived Column1:52
run. Yep, failed asserting that null is false. Okay, let's update our channel's migration, and we'll say here, I need a Boolean for the archived status, and of course, it should default to false. By default, it is not archived. So we'll give that another run, hmm, and it's not being reflected. I wonder, this might just be that we have to get fresh data. Yeah, so at the moment, we just have what came from the ModelFactory, but if we said fresh, there we go. Now we can see it.
fresh, there we go. Now we can see it. So we'll do this in two ways, but to start, we'll say $channel fresh $archived should be false, and now it says failed asserting that zero is false. Okay, so we need to cast that. Back to $channel, and at the top, hmm, maybe right here, we're going to cast the archived status and make sure it's always returned to us as a Boolean. Let's give it another run, and we do get green. But next, I want to go to our ModelFactory for a Channel, here we go, and we'll add it here so that I don't have to fetch a fresh copy.
Archive Model Method2:52
But next, I want to go to our ModelFactory for a Channel, here we go, and we'll add it here so that I don't have to fetch a fresh copy. archived will be false. Okay, so now, yes, we get a run here, but even if I get rid of the fresh status, it's still going to pick up on that. So we run it again, and that returns green. Next though, why don't we call a method on Channel, like archive, and if we do that, at that point, it should be changed to true. All right, let's give that a run, and archive doesn't exist, so that's our next step. We'll do it right here, and to archive a Channel, we'll just say this->update(['archived' => true]).
Admin Update Archived3:20
All right, let's give that a run, and archive doesn't exist, so that's our next step. We'll do it right here, and to archive a channel, we'll just say this->update(['archived' => true]);. Let's give it another run, and now we're at green. So that part, that part of the job is working. Next, let's go back to the edit section, and what I want to say is, if I update the channel, but I set its status to archived, well, let's make sure that does what we expect. So I think we have a feature test for Channel administration, and we do, yep, that's returning green. Let's find one. An Administrator can edit an existing Channel.
Let's find one. An Administrator can edit an existing Channel. So this is pretty close to what we want. If we sign in an Administrator, and we submit a PATCH request to update the current Channel, and here's the data we pass through, well, if we visit that page, we should see the updated information. Let's do another one here, and say an Administrator can mark an existing Channel as archived. Okay, so let's see what we can grab here. We'll say, paste all this in, given I'm an Administrator, and I hit the endpoint to update this Channel that I have here, and specifically, I want to set its status, I'm sorry, archived.
Okay, so does that all make sense? If we're an Administrator, and we have a Channel that's not archived, well, if we hit this endpoint to update the Channel, and we set archived in the request to true, well that should signal to the controller we need to archive that Channel. So our verification is, just fetch that Channel again, and make sure that the column has been updated. And at the moment, it's failing. Okay, let's go to ChannelsController within our admin namespace, and here's the method to update a Channel. So to start, we now accept the archived status, and that's required, and that should be a
Fix Unique Validation5:42
to update a Channel. So to start, we now accept the archived status, and that's required, and that should be a boolean. But actually, on this note, I think I remember something earlier. When I merged this PR before, if I just update it, and I don't change anything, it fails. The name has already been taken. So the issue here, if we switch back, is this is a common pitfall actually. So you have validation that you want to reuse, right? So here you're saying, when I update this channel, well, we need to make sure that the channel name is unique.
So here you're saying, when I update this channel, well, we need to make sure that the channel name is unique. But it's not unique, because this channel name already exists in the database. So we need a way to say, well, ignore the current one. We're not interested in the current row, but make sure that it doesn't conflict with any other rows. Okay, we can do that. Using the third argument here, it gets a little tricky. There's also a rule that we can use. So let's switch to the array syntax, and we'll say rule, and let me show you this real quick.
There's also a rule that we can use. So let's switch to the array syntax, and we'll say rule, and let me show you this real quick. So here are some of the methods we can call on this. It's just a little wrapper to make it a little easier. So we'll say rule unique, and then if we take a look at that, there should be an ignore method on it. So that's our way of saying, okay, this name column should be unique, but we can ignore this particular ID. So let's come back, and we'll say rule unique on the channels table, but you can ignore the current channel.
So let's come back, and we'll say rule unique on the channels table, but you can ignore the currentChannel. And do we need the ID? Let's see if we can get away with just the channel. So let's come back, see if we can update it, and no, okay, I guess it's not going to fetch the ID. So let's say ignore the currentChannel. One more time, update it, and now that works. Okay. So that's something we should update in a different commit, but it's okay for now.
Okay. So that's something we should update in a different commit, but it's okay for now. Anyways, we're also going to accept this archived request attribute, and remember, now that we're validating it, that will be returned from this method call that we then instantly pass the update. We're just inlining it there. So anyways, if we give this a run, it's still going to fail. So the validation is failing. Updated channel. Oh yeah, that's because we accidentally got rid of this, like so.
Updated channel. Oh yeah, that's because we accidentally got rid of this, like so. So let's give that a run. There we go. Now it is working. What we can even do is once again, I often do this just to show you the before and after. So channelArchived should be false. There we go. Then we perform this action, we hit this endpoint with that data, and here is what that should change.
Update Admin Form8:21
Then we perform this action, we hit this endpoint with that data, and here is what that should change. It makes it very clear. And in this case, I don't even need the variable name. So now that we have proof that works, we can just go and update the view. Let's go into, where is it? Here's our edit channel view, and you'll see whoever created the original PR extracted a form partial, and actually two things there. You'll notice, because we're reusing that form, we can basically make use of this twice, and you'll see this button is add, which is great.
You'll notice, because we're reusing that form, we can basically make use of this twice, and you'll see this button is add, which is great. But if we edit one, it still says add, which isn't quite what you want. So one thing you can do here is make that configurable. What if we said the button text for create, well, that should be add. But maybe if you edit, we'll say the button text should be update channel, like so. So now, on our form, we can just check for that. If there's button text, then just use whatever was passed in. Otherwise, we'll say add channel. All right.
Otherwise, we'll say addChannel. All right. So if we give that a refresh, now it says update, but if we create a new Channel, now it says add. Just little things like that. Anyways, the next step, we need to add a new section here. So we'll say add a form group, and then I need a select with two options within it. So the select will be archived, and then we should have a label for that as well, actually. So we'll say archived. And we'll actually say status in this case, because we're going to have a status for active,
So we'll say archived. And we'll actually say status in this case, because we're going to have a status for active, or is it archived? And the value will be, well, if archived is set to 0, then it's currently active. If archived is set to 1, then it's archived. Now in this case, whoops, we didn't need that. So if we give it a refresh, there we go. Maybe we can even add a class of form-control to make that a block. There we go. But next, of course, we want that to be dynamic.
There we go. But next, of course, we want that to be dynamic. So for example, if the archived column is set to true, then we need to make sure we update the selected option there. We can do it dynamically by saying channel.archived. If so, then it's not selected. Otherwise, it is selected. Then we'll do the same thing here. So in this case, if the channel is archived, then this is the one that should be selected. Otherwise, nothing.
Migrate and Seed Admin10:54
So in this case, if the channel is archived, then this is the one that should be selected. Otherwise, nothing. Alright, refresh, and I want to try this out, but we haven't yet refreshed our migration for local development. We've only done it for testing. So let's do php artisan migrate:fresh. And actually as part of that, real quick, let's go to our UserSeeder. Does that give us an administrator? So it clears out the users table, and it does give us one, but we can never sign in as that person because we don't know what the password will be set to.
So it clears out the users table, and it does give us one, but we can never sign in as that person because we don't know what the password will be set to. So let's just create our own called password. And that way you don't have to manually register an administrator. Anyways, refresh the migrations, and then cd, I'm sorry, db cd the database. So now if we come back home, there we go. Anyways, at this point, if we go to channels and refresh, now we do have this archived column. So let's make sure the select is working. This one called Apparium.
So let's make sure the select is working. This one called Apparium. Here it is. Let's just manually update it. And if I now give it a refresh, this should now say archived. And it does exactly what we want. So if we think about it, I think this is mostly everything we need to do. On our form, we now have a section for declaring the status. And notice we correspond whichever one you select, active or archived, to the value we want to insert into the database.
And notice we correspond whichever one you select, active or archived, to the value we want to insert into the database. So zero or one in this case. Next, in ChannelsController, we've already written tests for this to prove that the flow from this endpoint forward works. We make sure you gave us a Boolean. And remember, for Boolean, Laravel's validation component, it will accept one or zero or true or false. Any of those will be fine. Anyways, we update the channel, we clear the cache, and we return a response.
Any of those will be fine. Anyways, we update the channel, we clear the cache, and we return a response. So I think at this point, we might be good. At the moment, we marked this archived, but let's change it to active. And if we switch back, now that works. And we can see it here. But if we archive it again, refresh, now that's working. So only one or two more things. To start, if this is now archived, it might be nice to have some visual feedback there. So let's go to code and let's see.
To start, if this is now archived, it might be nice to have some visual feedback there. So let's go to code and let's see. Here's our index view. Looks like they used a table here. Looks like we need some indentation. And here's the thead. So that's all this stuff here. And then we have all of the rows. So filter through all of the channels. Can we, with Bootstrap, isn't there classes like that you can use?
Hide Archived Channels14:17
If somebody maybe visits a thread with a direct link or through search, maybe you still allow that, but you don't want it in your main results. It just depends on what you want to do there. Let's assume we want to hide it. So when we're done, this should no longer display. All right, well, let's see. If we go to the ChannelDropdown view component, one thing I've done, I think behind the scenes, is I make that component fetch the channels itself. So you can see we hit this endpoint. Let's go to ChannelsController for the API.
So you can see we hit this endpoint. Let's go to ChannelsController for the API. And here's what I have right now. I cache it, but then we just grab all channels. We order them by name in ascending order. So that's how you get in ascending order all channels. So if we want to exclude that, we could always say Channel::where(archived, false). Yeah, we could do that. So let's manually clear the cache. Come back to Safari, give it a refresh.
So let's manually clear the cache. Come back to Safari, give it a refresh. And now this one, Epirium, no longer shows up. And what's the other one there? However you say it, that's excluded as well. So that's a nice and easy way to do this. If we come up to new thread, let's see. Well, now it's showing up there as well, because we're actually performing this query in a couple different places, which might mean we might want to centralize it. And that's where something like a repository or just extracting any class to perform the
couple different places, which might mean we might want to centralize it. And that's where something like a repository or just extracting any class to perform the query can be useful. Let's see what's going on there. In ThreadsController create, here's the action that displays this. Well, yeah, here, once again, we're fetching channels separately. So I think what I'm going to do is if we're only fetching these channels in two places total, it's a little duplication, but it's okay. It's simpler to keep it as it is. If I start finding in more areas though, we get channels and we keep having to remind
