dirtyoceanassociation's avatar

Laravel - Storing/ret images for blog project

Project link: if anyone needs the github project I can link it. wont let me post here cause its my first day

My apologies I really wanna learn laravel.

(Please forgive me as I am a novice to Laravel) I fonund a tutorial project I am using it to study the laravel system.

This project is a complete blog for laravel, the only issue is it uses URL's to fetch images rather than an upload/retrieve setup. I will post complete code in hopes to get some answers, I've been studying on the issue however, the way this code I formatted is a little different from your normal how to documentation.

------------------------------------------BlogsController

?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Http\Requests\BlogsUpdateRequest; use App\Http\Requests\DatatablesRequest; use App\Modules\Blogs\BlogsService; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Http\Response;

class BlogsController extends Controller {

public function __construct(
    private readonly BlogsService $service
)
{

}

public function index(DatatablesRequest $request): JsonResponse
{
    try {
        return response()->json($this->service->index($request->data()));
    } catch (Exception $error) {
        return response()->json(
            [
                "exception" => get_class($error),
                "errors" => $error->getMessage()
            ],
            Response::HTTP_BAD_REQUEST
        );
    }
}

public function get(int $id): JsonResponse
{
    try {
        return response()->json($this->service->get($id));
    } catch (Exception $error) {
        return response()->json(
            [
                "exception" => get_class($error),
                "errors" => $error->getMessage()
            ],
            Response::HTTP_BAD_REQUEST
        );
    }
}

public function update(BlogsUpdateRequest $request): JsonResponse
{
    try {
        return response()->json($this->service->update($request));
    } catch (Exception $error) {
        return response()->json(
            [
                "exception" => get_class($error),
                "errors" => $error->getMessage()
            ],
            Response::HTTP_BAD_REQUEST
        );
    }
}

public function delete(int $id): JsonResponse
{
    try {
        $this->service->delete($id);
        return response()->json(null, Response::HTTP_NO_CONTENT);
    } catch (Exception $error) {
        return response()->json(
            [
                "exception" => get_class($error),
                "errors" => $error->getMessage()
            ],
            Response::HTTP_BAD_REQUEST
        );
        
    }
}

}

-------------------------------------BlogsupdateRequest

?php

declare(strict_types=1);

namespace App\Http\Requests;

use App\Models\Blogs; use Illuminate\Foundation\Http\FormRequest;

class BlogsUpdateRequest extends FormRequest { public function rules(): array { return [ "id" => "nullable|numeric", "is_trending" => "required|boolean", "author" => "required|string", "author_image_url" => "required|string", "image_url_portrait" => "required|string", "image_url_landscape" => "required|string", "date" => "required|string", "title" => "required|string", "tags" => "required|string", "description" => "required|string", "content" => "required|string", ]; }

public function data(): array
{
    $id = $this->input("id", null);

    return [
        "id" => ($id === null) ? null : (int)$id,
        "is_trending" => $this->input("is_trending", 0),
        "author" => $this->input("author"),
        "author_image_url" => $this->input("author_image_url"),
        "image_url_portrait" => $this->input("image_url_portrait"),
        "image_url_landscape" => $this->input("image_url_landscape"),
        "date" => $this->input("date"),
        "url" => $this->generateUrl($this->input("title")),
        "title" => $this->input("title"),
        "tags" => array_map(function($row) {
            return trim($row);
        }, explode(",", $this->input("tags", []))),
        "description" => $this->input("description"),
        "content" => $this->input("content"),

    ];

}


private function generateUrl(string $title): string {
    $newUrl = trim(strtolower($title));
    $newUrl = str_replace("  ", " ", $newUrl);
    $newUrl = str_replace(" ", "-", $newUrl);
    return preg_replace("/[^A-Za-z0-9\-]/", "", $newUrl);
}

}

------------------------------------------BlogsResource

?php

declare(strict_types=1);

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\JsonResource;

class BlogsResource extends JsonResource {

public function toArray($request)
{
    return [
        "id" => $this->id,
        "url" => $this->url,
        "is_trending" => $this->is_trending,
        "author" => $this->author,
        "author_image_url" => $this->author_image_url,
        "image_url_portrait" => $this->image_url_portrait,
        "image_url_landscape" => $this->image_url_landscape,
        "title" => $this->title,
        "date" => $this->date,
        "description" => $this->description,
        "content" => $this->content,
        "tags" => implode(
            ", ",
            array_map(function($row) {
                return $row["tag"];
            }, json_decode(json_encode($this->tags), true))

        )

    ];


}

}

----------------------------------------BlogsModel ...

?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Blogs extends Model {

protected $table = "blogs"; protected $fillable = [ "id", "is_trending", "author", "author_image_url", "image_url_portrait", "image_url_landscape", "title", "date", "description", "content", "created_at", "updated_at",

];

public function tags (){ return $this->hasMany(BlogTags::class); }

}

----------------------------------BlogsRepository

?php

declare(strict_types=1);

namespace App\Modules\Blogs;

use App\Http\Requests\BlogsUpdateRequest; use App\Models\Blogs; use App\Models\BlogTags;

class BlogsRepository { const RECENT_BLOGS_LIMIT = 5;

public function getTotalCount(): int
{
    return Blogs::all()->count();
}

public function UIList(int $page, int $pageLength, array $filters = []): array
{
    if ($filters !== []) {
        return Blogs::with(["tags"])
            ->where($filters)
            ->where("id", ">", 0)
            ->limit($pageLength)
            ->offset(($page - 1) * $pageLength)
            ->get()
            ->toArray();
    }

    return Blogs::with(["tags"])
        ->where("id", ">", 0)
        ->limit($pageLength)
        ->offset(($page - 1) * $pageLength)
        ->get()
        ->toArray();
}

public function UIListRecent(): array
{
    return Blogs::with(["tags"])
        ->where("id", ">", 0)
        ->limit(self::RECENT_BLOGS_LIMIT)
        ->orderBy("created_at", "desc")
        ->get()
        ->toArray();
}

public function get(int $id): Blogs
{
    return Blogs::with("tags")->findOrFail($id);
}

public function update(BlogsUpdateRequest $request): Blogs
{
    $data = $request->data();

    $newBlog = ($data["id"] === null)
        ? new Blogs()
        : $this->get($data["id"]);

    $newBlog->url = $data["url"];
    $newBlog->is_trending = $data["is_trending"];
    $newBlog->author = $data["author"];
    $newBlog->author_image_url = $data["author_image_url"];
    $newBlog->title = $data["title"];
    $newBlog->date = $data["date"];
    $newBlog->description = $data["description"];
    $newBlog->content = $data["content"];
    $newBlog->image_url_landscape = $data["image_url_landscape"];
    $newBlog->image_url_portrait= $data["image_url_portrait"];

    $newBlog->save();

    BlogTags::where("blogs_id", $newBlog->id)->delete();
    $toInsertTags = [];
    for ($i = 0; $i < count($data["tags"]); $i++) {
        $toInsertTags [] = [
            "tag" => $data["tags"][$i],
            "blogs_id" => $newBlog->id
        ];
    }
    BlogTags::insert($toInsertTags);

    return $this->get($newBlog->id);
}

public function delete(int $id): void
{
    BlogTags::where("blogs_id", $id)->delete();
    Blogs::findOrFail($id)->delete();
}

public function getByUrl(string $url) : Blogs
{
    return Blogs::where("url", $url)->first();
}

}

----------------------------- Form.Blade

<div class="modal-dialog modal-fullscreen text-dark">
    <div class="modal-content">
        <div class="modal-content container" style="overflow-y:scroll;">
            <div class="row">
                <div class="col-12 text-end">
                    <span type="button" class="btn-close" data-bs-dismiss="modal"></span>
                </div>
                <div class="col-12">
                    <div id="BlogsErrorsContainer" class="alert alert-danger errorContainer" style="display:none;">
                        <h5 class="font-weight-bolder">Error!</h5>
                        <ul></ul>
                    </div>
                </div>
                <div class="col-12">
                    <hr>
                    <h5 class="modal-title font-weight-bolder">New Blog</h5>
                    <hr>
                    <form id="BlogsForm">
                        <input type="hidden" class="form-control" id="itemId">
                        <div class="row">
                            <div class="col-12 mb-3">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" value="1" id="blogsIsTrending">
                                    <label class="form-check-label">Trending</label>
                                </div>
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Title</label>
                                <input type="text" class="form-control" id="blogsTitle">
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Date</label>
                                <input type="text" class="form-control" id="blogsDate">
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Author Name</label>
                                <input type="text" class="form-control" id="blogsAuthorName">
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Author Image URL</label>
                                <input type="text" class="form-control" id="blogsAuthorImageUrl">
                            </div>
                            <div class="col-12">
                                <hr>
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Image Url Landscape</label>
                                <input type="text" class="form-control" id="blogsImageUrlLandscape">
                            </div>
                            <div class="col-lg-6 col-md-6 col-sm-12">
                                <label class="form-label font-weight-bolder">Image Url Portrait</label>
                                <input type="text" class="form-control" id="blogsImageUrlPortrait">
                            </div>
                            <div class="col-12">
                                <hr>
                            </div>
                            <div class="col-12">
                                <label class="form-label font-weight-bolder">Tags</label>
                                <input type="text" class="form-control" id="blogsTags">
                                <div class="form-text">Tags must be seperated by a comma. Example: <i>Laravel, Hosting</i></div>
                            </div>
                            <div class="col-12">
                                <hr>
                            </div>
                            <div class="col-12">
                                <label class="form-label font-weight-bolder">Description</label>
                                <textarea rows="2" type="text" class="form-control" id="blogsDescription"></textarea>
                                <div class="form-text">1-3 sentences max.</div>
                            </div>
                            <div class="col-12 mb-5">
                                <label class="form-label font-weight-bolder">Content</label>
                                <div id="blogsContent"></div>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
        <div class="modal-footer container">
            <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">Close</button>
            <button id="SubmitBlogsForm" type="button" class="btn btn-sm btn-primary">Save Changes</button>
        </div>
    </div>
</div>
let formSubmitted = false; let blogsEndpoint = "/api/dashboard/blogs"; let quillToolbar = [ ['bold', 'italic', 'underline', 'strike'], // toggled buttons ['blockquote', 'code-block'],
    [{
        'header': 1
    }, {
        'header': 2
    }], // custom button values
    [{
        'list': 'ordered'
    }, {
        'list': 'bullet'
    }],
    [{
        'script': 'sub'
    }, {
        'script': 'super'
    }], // superscript/subscript
    [{
        'indent': '-1'
    }, {
        'indent': '+1'
    }], // outdent/indent
    [{
        'direction': 'rtl'
    }], // text direction

    [{
        'size': ['small', false, 'large', 'huge']
    }], // custom dropdown
    [{
        'header': [1, 2, 3, 4, 5, 6, false]
    }],

    [{
        'color': []
    }, {
        'background': []
    }], // dropdown with defaults from theme
    [{
        'font': []
    }],
    [{
        'align': []
    }],

    ['clean'] // remove formatting button
];
let quill = new Quill(
    "#blogsContent", {
        modules: {
            toolbar: quillToolbar
        },
        placeholder: "Compose an epic ...",
        theme: "snow"
    }
);

function clearForm() {
    $("#itemId").val(null);
    $("#blogsTitle").val(null);
    $("#blogsAuthorName").val(null),
        $("#blogsAuthorImageUrl").val(null);
    $("#blogsImageUrlLandscape").val(null);
    $("#blogsImageUrlPortrait").val(null);
    $("#blogsTags").val(null);
    $("#blogsDescription").val(null);
    $("#blogsDate").val(null);
    $("#blogsIsTrending").prop("checked", false);
    $("#blogsContent").val(null);
    quill.setContents([{
        insert: "\n"
    }]);
    clearErrors();
}

function clearErrors() {
    $("#BlogsErrorsContainer ul").empty();
    $("#BlogsErrorsContainer").hide();
}

function showErrors(errorsList = []) {
    Object.keys(errorsList).forEach(key => {
        $("#BlogsModal .errorContainer ul").append(
            "<li><b>" + key + ": </b>" + errorsList[key] + "</li>"
        );
        $("#BlogsModal .errorContainer").show();
    });
}

$(document).ready(function() {
    $("#SubmitBlogsForm").click(function() {
        event.preventDefault();
        clearErrors();

        if (formSubmitted !== true) {
            formSubmitted = true;
            $.ajax({
                type: "POST",
                url: blogsEndpoint,
                dataType: "json",
                contentType: "application/json; charset=utf-8",
                data: JSON.stringify({
                    id: $("#itemId").val(),
                    title: $("#blogsTitle").val(),
                    author: $("#blogsAuthorName").val(),
                    author_image_url: $("#blogsAuthorImageUrl").val(),
                    image_url_landscape: $("#blogsImageUrlLandscape").val(),
                    image_url_portrait: $("#blogsImageUrlPortrait").val(),
                    tags: $("#blogsTags").val(),
                    description: $("#blogsDescription").val(),
                    date: $("#blogsDate").val(),
                    is_trending: $("#blogsIsTrending").prop("checked"),
                    content: quill.root.innerHTML.trim(),
                }),
                success: function(response) {
                    formSubmitted = false;
                    $BlogsModal.hide();
                    $("#dataTable").DataTable().ajax.reload();
                }
            }).fail(function(response) {
                formSubmitted = false;
                if (response.status === 422) {
                    showErrors(response.responseJSON["errors"]);
                } else {
                    showErrors({
                        Error: "Could not process your request! Please try again later."
                    });
                }
            });
        }

    });
});
0 likes
1 reply
dirtyoceanassociation's avatar

https github.com/emad-zaamout/laravel-9-complete-course-blog-implementation

github project

Please or to participate in this conversation.