Nam Còi
  • Trang chủ
  • Lập Trình Website
  • Khóa Học
  • Động Lực
  • Tuyển Dụng
No Result
View All Result
  • Trang chủ
  • Lập Trình Website
  • Khóa Học
  • Động Lực
  • Tuyển Dụng
No Result
View All Result
Nam Còi
No Result
View All Result
Home Lập Trình Website Laravel

Upload Multiple Files và đính kèm vào Task sử dụng Teamwork API thông qua PHP Laravel API

Nguyễn Hải Nam by Nguyễn Hải Nam
22/02/2023
in Laravel, PHP
50
teamwork_logo

teamwork_logo

0
SHARES
94
VIEWS

Sử dụng back-end PHP Laravel API để Upload Multiple Files và đính kèm files vào task sử dụng Teamwork API có thể gây nhiều rắc rối. Bài viết này sẽ hướng dẫn thiết lập API, tương thích với Teamwork API, thiết lập Feature Test và ví dụ trên Postman

teamwork-logo.png
teamwork-logo.png

Upload Multiple Files sử dụng File uploading via the API (Classic)

Bước đầu tiên, thiết lập API route trong file api.php, với phương thức POST:

                # Uploading Files
                # POST - /teamwork/uploading-files
                Route::post('uploading-files',
                [
                    TeamworkController::class,
                    'uploadingFiles'
                ])->name('uploading-files');

Tiếp theo, trong phần Controller, chỉ là các logic đơn giản như bên dưới, mình cần phải ẩn một số thông số vì lý do thông tin bảo mật cho dự án:

    public function uploadingFiles(
        UploadingFilesRequest $request
    ): Response
    {
        try {
            $files = $request->file('files');

            $teamworkUrl = env('TEAMWORK_URL', 'YOUR_TEAMWORK_URL_HERE');
            $teamworkEndpointFirstStep = "{$teamworkUrl}/pendingFiles.json";
            $teamworkUserServiceRequestProjectId = env('TEAMWORK_USER_SERVICE_REQUEST_PROJECT_ID', YOUR_PROJECT_ID_HERE);
            $teamworkEndpointSecondStep = "{$teamworkUrl}/projects/{$teamworkUserServiceRequestProjectId}/files.json";

            $authorization = 'Basic ' . base64_encode(env('TEAMWORK_API_TOKEN', 'YOUR_TOKEN_HERE'));

            $result = [];
            foreach ($files as $index => $file) {
                $fileName = $file['file']->getClientOriginalName();
                $filePath = $file['file']->getPathname();
                $preSigned = Http::withHeaders(
                    [
                        'Authorization' => $authorization,
                    ]
                )->attach('file',file_get_contents($filePath),$fileName)->post($teamworkEndpointFirstStep);

                $fileRef = json_decode($preSigned->body())->pendingFile->ref;

                $data = [
                        'description' => $fileName,
                        'pendingFileRef' => $fileRef,
                ];

                $response = Http::withHeaders(
                    [
                        'Authorization' => $authorization,
                    ]
                )->post($teamworkEndpointSecondStep,[
                    'file' => $data
                ]);

                $result[$index] = json_decode($response,TRUE)['fileId'];
            }
            return response_success($result);
        } catch (Exception $e) {
            return response_error("[Teamwork Controller] Upload Files Function - {$e}");
        }
    }

Lưu ý: API File uploading via the API (Preferred) https://apidocs.teamwork.com/docs/teamwork/d29b91f3e6558-file-uploading-via-the-api-preferred hoàn toàn không work sau khi mình thử nhiều cách với phương thức mình sử dụng, nhưng sử dụng Javascript thì hoạt động ổn. Vì vậy, chúng ta phải sử dụng: File uploading via the API (Classic): https://apidocs.teamwork.com/docs/teamwork/86ecebd6161af-file-uploading-via-the-api-classic

Theo như sự hỗ trợ từ phía Teamwork Support thì:

Unfortunately we’ve lost our API Support Team and I’ll be stepping in here from our Technical Services team to assist.

For Classic Upload; https://apidocs.teamwork.com/docs/teamwork/86ecebd6161af-file-uploading-via-the-api-classic

Step 1 – Create a Pending File Handle
The first thing to do is to get the actual file uploaded to Teamwork .
To do this you make an API Call to POST /pendingFiles.json. The actual file contents are sent via a form field called “file”.

If this was successful you will receive a Status Code of 201 (Created) and a structure containing a reference code.
{ “pendingFile”: { “ref” : “tf_xxxxxxxxxxxxxxxx” } }

The ref bit is the important part and is the Pending File Handle

Step 2 – Create an actual file object using the Pending File Handle
The next step is to create the actual file object in a project on your Teamwork account.
To do this you make an API Call to POST /projects/{id}/files.json where {id} is the ID of the project you want to create the file in.
{
“file”: {
“description”: “Optional string describing the file”,
“category-id”: “ID of the category you to create the file in. Pass 0 if no category”,
“category-name”: “String if you want to create a new category – Pass category-id=0 also”,
“pendingFileRef”: “tf_xxxxxxxxxxxxxxxx”
}
}

In the JSON packet above you substitute the Pending File Ref you received back from the POST /pendingFiles.json API Call in Step 1.

Teamwork API Team Support

Ngoài ra, chúng ta cũng cần thiết lập Request UploadingFilesRequest như bên dưới:

    public function rules(): array
    {
        return [
            'files' => 'required|array',
            'files.*.file' => 'required',
        ];
    }


    /**
     * Body Parameters of this form
     *
     * @return array
     */
    final public function bodyParameters(): array
    {
        return [
            'files' => [
                'description' => 'The array file of this meeting',
                'example' => [],
            ],
            'files.*.file' => [
                'description' => 'The file need to upload',
            ],
        ];
    }

Dễ dàng nhận thấy rằng, dạng files upload lên sẽ là:

files[]
files[0][file]
files[1][file]
...

Tiếp theo, chúng ta viết Feature Test, ý tưởng là test về permission và test upload 2 files lên Teamwork:

    public function testTeamworkUploadFiles()
    {
        $apiPath = self::API_PATH . "/uploading-files";
        $company = Company::query()
            ->inRandomOrder()
            ->first();
        $user = $company->owner;

        # Unauthenticated
        $response = $this->post($apiPath);
        $response->assertUnauthorized()
            ->assertJson(TestConstant::JSON_UNAUTHENTICATED);

        # Successfully
        # Company
        Storage::fake('files');
        $files = [];
        $files[0]['file'] = UploadedFile::fake()
            ->createWithContent("test-1.pdf", 'abc');

        $files[1]['file'] = UploadedFile::fake()
            ->createWithContent("test-2.pdf", 'abc');
        $data = [
            'files' => $files,
        ];
        # Successfully
        $response = $this->actingAs($user, 'api')
            ->post($apiPath, $data);

        $response->assertSuccessful()
            ->assertJson(TestConstant::JSON_SUCCESS)
            ->assertJsonStructure(TestConstant::JSON_STRUCTURE_VALUES);
    }

Cuối cùng, chúng ta sẽ test trên Postman để gửi ví dụ này cho Front-end Team hoặc các bên đối tác khác.
Điểm lưu ý ở đây là key của form phải là: files[0][file], thay vì chỉ là files hoặc files[0].file:

test-postman-upload-multiple-files-teamwork-api
test-postman-upload-multiple-files-teamwork-api

Tạo task trên Teamwork đính kèm files đã upload với API Create a Task

Đầu tiên, thiết lập API trên Laravel:

                # Create Additional Service Task
                # POST - /teamwork/additional-services
                Route::post('additional-service',
                [
                    TeamworkController::class,
                    'createAdditionalService'
                ])->name('additional-service');

Tiếp theo, viết logic trong Controller (không sử dụng Service để tiết kiệm thời gian, mặc dù không phải là best practice), chúng ta sẽ sử dụng Teamwork API https://apidocs.teamwork.com/docs/teamwork/cd8948166b1b1-create-a-task

    final public function createAdditionalService(
        CreateAdditionalServiceRequest $request
    ): Response {
        try {
            $teamworkUrl = env('TEAMWORK_URL', 'YOUR_URL_HERE');
            $teamworkUserServiceInboxTaskListId = env('TEAMWORK_USER_SERVICE_INBOX_TASK_LIST_ID',YOUR_LIST_ID_HERE);
            $teamworkEndpoint = "{$teamworkUrl}/tasklists/{$teamworkUserServiceInboxTaskListId}/tasks.json";
            $authorization = 'Basic ' . base64_encode(env('TEAMWORK_API_TOKEN', 'YOUR_TOKEN_HERE'));
            $data = [
                "todo-item" => [
                    "content" => env('APP_ENV','unknown') . " - " . $request->input('content'),
                    "description" => $request->input('description'),
                    "notify" => env('TEAMWORK_NOTIFY',FALSE),
                    "responsible-party-id" => env('TEAMWORK_RESPONSIBLE_PARTY_ID_USER_SERVICE_REQUEST',"YOUR_RESPONSIBLE_PARTY_ID_HERE"),
                    "attachments" => $request->input('attachments') ?? ""
                ]
            ];

            $response = Http::withHeaders(
                [
                    'Authorization' => $authorization,
                ]
            )->post($teamworkEndpoint, $data);

            return response_success(json_decode($response, true));
        } catch (Exception $e) {
            return response_error("[Teamwork Controller] Create Additional Service On Teamwork Function - {$e}");
        }
    }

File Request CreateAdditionalServiceRequest

    public function rules(): array
    {
        return [
            'content' => 'required|string',
            'description' => 'required|string',
            'attachments' => 'nullable|string',
        ];
    }

    /**
     * Body Parameters of this form
     *
     * @return array
     */
    final public function bodyParameters(): array
    {
        return [
            'content' => [
                'description' => 'The title of a Teamwork Task',
                'example' => 'Nick has requested for Perform a capital increase',
            ],
            'description' => [
                'description' => 'The description of a Teamwork Task',
                'example' => '#### Requester\n          \n * Nick\n        \n\n#### Order Data\n',
            ],
            'attachments' => [
                'description' => 'The file Id(s) attached to a Teamwork Task, refer to API Teamwork Upload Files',
                'example' => "1343151,1343152"
            ]
        ];
    }

Theo như hướng dẫn từ tài liệu của Teamwork How do I upload a file with the API? https://apidocs.teamwork.com/docs/teamwork/f92e3220710ec-how-do-i-upload-a-file-with-the-api, họ hướng dẫn đính kèm files với field tên là “pendingFileAttachments“, tuy nhiên, field này chỉ sử dụng cho API File uploading via the API (Preferred): https://apidocs.teamwork.com/docs/teamwork/d29b91f3e6558-file-uploading-via-the-api-preferred.

Để sử dụng đúng, chúng ta phải dùng field “attachments” đi cùng với API File uploading via the API (Classic) https://apidocs.teamwork.com/docs/teamwork/86ecebd6161af-file-uploading-via-the-api-classic, data gửi lên sẽ có dạng: “file_id_1,file_id_2”, tương ứng với ví dụ ở trên thì chúng ta phải lấy giá trị “values” và sử dụng built-in function implode của PHP để nối chuỗi nếu sử dụng Feature Test như bên dưới:

    public function testCreateAdditionalService()
    {
        $apiPath = self::API_PATH . "/additional-service";
        $company = Company::query()
            ->inRandomOrder()
            ->first();
        $user = $company->owner;

        # Unauthenticated
        $response = $this->post($apiPath);
        $response->assertUnauthorized()
            ->assertJson(TestConstant::JSON_UNAUTHENTICATED);

        # Successfully
        $request = [
            "content" => "{$company->name} has requested for Perform a capital increase",
            "description" => "test",
        ];

        $response = $this->actingAs($user, 'api')
            ->post($apiPath, $request);

        $response->assertSuccessful()
            ->assertJson(
                array_merge(
                    TestConstant::JSON_SUCCESS,
                    [
                        'data' => [
                            'STATUS' => 'OK',
                        ]
                    ]
                )
            )
            ->assertJsonStructure(
                array_merge(
                    TestConstant::JSON_STRUCTURE,
                    [
                        'data' => [
                            'affectedTaskIds',
                            'id',
                            'STATUS',
                        ]
                    ]
                )
            );

        # Successfully with Files
        Storage::fake('files');
        $files = [];
        $files[0]['file'] = UploadedFile::fake()
            ->createWithContent("test-1.pdf", 'abc');

        $files[1]['file'] = UploadedFile::fake()
            ->createWithContent("test-2.pdf", 'def');
        $data = [
            'files' => $files,
        ];

        $uploadFilesApiPath = self::API_PATH . "/uploading-files";
        $filesUploaded = $this->actingAs($user, 'api')
            ->post($uploadFilesApiPath, $data);
        $fileIds = implode(",", $filesUploaded['data']['values']);

        $request = [
            "content" => "{$company->name} has requested for Perform a capital increase",
            "description" => "test",
            "attachments" => $fileIds,
        ];

        $response = $this->actingAs($user, 'api')
            ->post($apiPath, $request);

        $response->assertSuccessful()
            ->assertJson(
                array_merge(
                    TestConstant::JSON_SUCCESS,
                    [
                        'data' => [
                            'STATUS' => 'OK',
                        ]
                    ]
                )
            )
            ->assertJsonStructure(
                array_merge(
                    TestConstant::JSON_STRUCTURE,
                    [
                        'data' => [
                            'affectedTaskIds',
                            'id',
                            'STATUS',
                        ]
                    ]
                )
            );
    }

Logic của test sẽ là: test permission, test tạo task không có files đi kèm, test tạo task với files đi kèm và phải lưu ý về attachments field, test Postman như bên dưới:

test-postman-create-task-teamwork-api
test-postman-create-task-teamwork-api

Vì tài liệu của Teamwork API không rõ ràng cũng như các thiếu sự hỗ trợ rất nhiều nên team mình đã gặp một số khó khăn với Teamwork API như các lỗi bên dưới khi sử dụng API File uploading via the API (Preferred):

“value”: “Something went wrong: GuzzleHttp\Exception\ClientException: Client error: `PUT https://tw-bucket.s3-accelerate.amazonaws.com

“value”: “Something went wrong: Illuminate\\Http\\Client\\RequestException: HTTP request returned status code 403:\n<?xml version=\”1.0\” encoding=\”UTF-8\”?>\n<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calcul (truncated…)\n

Lỗi này có thể là do phía tương thích với AWS S3 signature mà package HTTP của Laravel hoặc thậm chí là curl đều không thể gửi files lên được.

Có liên quan

Tags: laravel apiteamwork api
Previous Post

Dự Án Crabada – Cua Siêu Đắt

Next Post

Webinoly – Trọn Bộ LEMP – Tối Ưu Hóa Máy Chủ Web Chạy NGINX

Nguyễn Hải Nam

Nguyễn Hải Nam

Mình là Nam - nick name ở nhà là Còi, trong tiếng Anh là Nick. Hiện nay, mình đang làm việc tại Axon Active Việt Nam với vị trí là Scrum Master. Ngoài ra, mình còn tham gia thỉnh giảng khóa học Lập Trình Ứng Dụng Website với PHP & MySQL. tại Softech Aptech Đà Nẵng.

Next Post
webinoly-logo

Webinoly - Trọn Bộ LEMP - Tối Ưu Hóa Máy Chủ Web Chạy NGINX

Comments 50

  1. pkr777login says:
    5 tháng ago

    Easy to get logged into pkr777login. Its a simple gaming site, with a decent selection. Check into logging in via pkr777login.

    Bình luận
  2. 777sxgameapk says:
    5 tháng ago

    777sxgameapk? That’s the spot! Got me hooked up. Give it a look-see 777sxgameapk

    Bình luận
  3. arionplaygcash says:
    4 tháng ago

    Arionplay and Gcash? That’s convenient! Depositing and withdrawing is a breeze. Makes playing a lot smoother. Definitely a plus in my book. arionplaygcash

    Bình luận
  4. binance says:
    4 tháng ago

    Thank you for your sharing. I am worried that I lack creative ideas. It is your article that makes me full of hope. Thank you. But, I have a question, can you help me?

    Bình luận
  5. Κωδικ αναφορ Binance says:
    4 tháng ago

    Reading your article helped me a lot and I agree with you. But I still have some doubts, can you clarify for me? I’ll keep an eye out for your answers. https://www.binance.info/pt-PT/register?ref=KDN7HDOR

    Bình luận
  6. seo experte says:
    3 tháng ago

    Really insightful post — Your article is very clearly written, i enjoyed reading it, can i ask you a question? you can also checkout this newbies in seo. thank you

    Bình luận
  7. webdesign freelancer says:
    3 tháng ago

    Thi is a great article no doubt about it, i just started following you and i enjoy reading this piece. Do you post often ? we have similar post on the german best freelancer platform called https://webdesignfreelancerdeutschland.de/ you can check it out if you want. Thank you

    Bình luận
  8. 🥴 Sex Dating. Sign Up >>>> yandex.com/poll/43o224okZdReGRb1Q8PXXJ?hs=c762d84936d9ec6067ac7cb0c7b22f59& Direct Message # XODM1305523 🥴 says:
    3 tháng ago

    n8bofm

    Bình luận
  9. lndfoiujki says:
    3 tháng ago

    ryewkjjdegwfxsqqjqgenikozmulgi

    Bình luận
  10. freiberufler says:
    3 tháng ago

    Thi is a great article no doubt about it, i just started following you and i enjoy reading this piece. Do you post often ? we have similar post on the german best freelancer platform you can check it out if you want. Trusted source by Google.Thank you

    Bình luận
  11. webdesign says:
    3 tháng ago

    this is an interesting article, i enjoy reading it, keep up the good work, do you post often, i want to start following you. my site is https://webdesignfreelancermunchen.de/ it is the top webdesign freelancer platform in Germany.

    Bình luận
  12. webdesign freelancer frankfurt says:
    3 tháng ago

    this is an interesting article, i enjoy reading it, keep up the good work, do you post often, i want to start following you. my site is https://webdesignfreelancermunchen.de/ it is the top webdesign freelancer platform in Germany.

    Bình luận
  13. seo freelancer says:
    2 tháng ago

    i enjoy reading your articles, it is simply amazing, you are doing great work, do you post often? i will be checking you out again for your next post. you can check out webdesignagenturnürnberg.de the best webdesign agency in nuremberg Germany

    Bình luận
  14. Открыть учетную запись в binance says:
    2 tháng ago

    Thank you for your sharing. I am worried that I lack creative ideas. It is your article that makes me full of hope. Thank you. But, I have a question, can you help me?

    Bình luận
  15. staphwin says:
    2 tháng ago

    It’s fascinating how gaming communities evolve! Platforms like staph win online casino are building spaces for connection, beyond just the games themselves – a real shift in the Philippines! The accessibility via app & web is smart too.

    Bình luận
  16. webdesign münchen says:
    2 tháng ago

    i enjoy reading your article, keep posting, i am a big fun and will continue following you, i am one of the best webdesign freelancer in München in Deutschland. https://webdesignfreelancermunchen.de/ check it out. Thank you

    Bình luận
  17. 🙇‍♀️ SEX tonight. Message me 👉 yandex.com/poll/UL3yvwR5LM6wboX9ibTVrW?hs=c762d84936d9ec6067ac7cb0c7b22f59& 🙇‍♀️ says:
    2 tháng ago

    0jdp78

    Bình luận
  18. 💥 my mistake linkparterg.cc/?u=w244y2&o=v264&utm=rnd?hs=c762d84936d9ec6067ac7cb0c7b22f59& 💥 says:
    2 tháng ago

    px01az

    Bình luận
  19. 🩸 Available BTC. Next - yandex.com/poll/WRVjqbSX2yscgTuFhiPPi5?hs=c762d84936d9ec6067ac7cb0c7b22f59& 🩸 says:
    2 tháng ago

    86r24k

    Bình luận
  20. eiffel tower reservations says:
    1 tháng ago

    I really enjoy reading this article, such an excellent piece, continue the good work, do you post often? you just got a fun from the eiffel tower paris. we are the best guide for paris eiffel tower. visit our site at https://eiffeltower-ticketparis.com/. thank you hope to hear from you.

    Bình luận
  21. 😜 Binance transfer. Get >> yandex.com/poll/YWfQL5UYL9NrETwvu6fWmf?hs=c762d84936d9ec6067ac7cb0c7b22f59& 😜 says:
    1 tháng ago

    hg1uz4

    Bình luận
  22. jl16 says:
    1 tháng ago

    Interesting points about responsible gaming! It’s good to see platforms like JL16 prioritizing legal compliance & RNG fairness – crucial for trust. Check out jl16 login for a regulated experience. Transparency is key in online casinos!

    Bình luận
  23. casino plus download says:
    1 tháng ago

    Where’s the best spot for a casino plus download? Need a secure link. Thanks in advance dudes! casino plus download

    Bình luận
  24. okbet agent says:
    1 tháng ago

    I’m wondering how to find a reliable okbet agent. Are there any trusted sources or websites for finding them? Tell me your ideas: okbet agent

    Bình luận
  25. mcw19.com app says:
    1 tháng ago

    Just downloaded the mcw19.com app. Easy to use so far. Hope I can win big from my phone! Download it here: mcw19.com app

    Bình luận
  26. 👄 Binance transaction. Get >> yandex.com/poll/YWfQL5UYL9NrETwvu6fWmf?hs=c762d84936d9ec6067ac7cb0c7b22f59& 👄 says:
    1 tháng ago

    i8wov2

    Bình luận
  27. 5522betcom says:
    4 tuần ago

    5522BetCom is pretty decent. Gave it a try, and my account opened fine. You can find access to this site through 5522betcom.

    Bình luận
  28. 77zcomcasino says:
    4 tuần ago

    Has anyone won anything big on 77zcomcasino? My luck’s been terrible lately! Gotta find a lucky place. 77zcomcasino

    Bình luận
  29. 82jllink says:
    4 tuần ago

    Need a working link! 82jllink seems to be it so I checked it out. Worth a shot? Yea. 82jllink

    Bình luận
  30. 📉 TRON Crypto Refund 2026 Available Today ➸➸ telegra.ph/Blockchaincom-03-17-5?hs=c762d84936d9ec6067ac7cb0c7b22f59& 📉 says:
    4 tuần ago

    kqqb79

    Bình luận
  31. 💵 +1.81919243841 Вitсоin Next ➼ 💵 says:
    4 tuần ago

    e6wtl3

    Bình luận
  32. CasinoBestUitbetalende says:
    7 ngày ago

    hey there and thank you in your information ? I?ve certainly picked up something new from proper here. I did on the other hand experience several technical points the use of this web site, as I skilled to reload the website many instances previous to I may get it to load correctly. I had been considering if your web host is OK? Not that I’m complaining, however slow loading circumstances instances will often have an effect on your placement in google and can harm your quality score if advertising and ***********|advertising|advertising|advertising and *********** with Adwords. Anyway I?m including this RSS to my email and can look out for much more of your respective exciting content. Make sure you update this again very soon..

    Bình luận
  33. EarnestJab says:
    7 ngày ago

    Terrific post however I was wondering if you could write a litte more on this subject? I’d be very thankful if you could elaborate a little bit further. Thanks!
    https://teletype.in/@avtobloggerua/yBdxoskAM8B

    Bình luận
  34. Dewalive says:
    6 ngày ago

    Great ? I should definitely pronounce, impressed with your web site. I had no trouble navigating through all the tabs and related information ended up being truly easy to do to access. I recently found what I hoped for before you know it at all. Quite unusual. Is likely to appreciate it for those who add forums or anything, web site theme . a tones way for your client to communicate. Nice task..

    Bình luận
  35. Dewalive says:
    6 ngày ago

    Thank you for another informative website. Where else may I am getting that kind of information written in such an ideal manner? I have a project that I’m just now running on, and I’ve been at the glance out for such information.

    Bình luận
  36. Ko Lanta piz says:
    5 ngày ago

    ко ланта ко ланта

    Bình luận
  37. Depobos says:
    5 ngày ago

    F*ckin? awesome things here. I?m very glad to see your post. Thanks a lot and i am looking forward to contact you. Will you kindly drop me a mail?

    Bình luận
  38. memory recall support says:
    5 ngày ago

    Thank you for any other informative website. The place else may I get that type of info written in such an ideal way? I’ve a undertaking that I’m just now working on, and I’ve been at the glance out for such info.

    Bình luận
  39. pumpkin seed supplement says:
    4 ngày ago

    hey there and thank you for your information ? I?ve certainly picked up anything new from right here. I did on the other hand experience some technical points the usage of this web site, since I skilled to reload the site lots of occasions prior to I could get it to load correctly. I were wondering in case your hosting is OK? Not that I’m complaining, however sluggish loading instances occasions will sometimes impact your placement in google and could harm your quality ranking if ads and ***********|advertising|advertising|advertising and *********** with Adwords. Well I?m including this RSS to my e-mail and can look out for much extra of your respective fascinating content. Make sure you replace this again very soon..

    Bình luận
  40. HowardCaf says:
    4 ngày ago

    раздвижной шкаф купе Приобретая предметы обстановки, стоит обращать внимание на их эстетическую привлекательность и, что более значимо, на функциональность в повседневной жизни. Разумнее всего заблаговременно спланировать габариты, внутреннее устройство, сырьевые компоненты и дизайнерское направление, чтобы покупка действительно соответствовала особенностям помещения. Такой подход оказывается особенно полезен для квартир с нетипичной планировкой, малогабаритных комнат и тех интерьеров, где требуется максимальная продуманность каждой детали.

    Bình luận
  41. custom mylar bags says:
    4 ngày ago

    This is the precise weblog for anybody who wants to search out out about this topic. You understand a lot its almost exhausting to argue with you (not that I actually would need?HaHa). You undoubtedly put a new spin on a subject thats been written about for years. Nice stuff, simply nice!

    Bình luận
  42. ceramic implants says:
    3 ngày ago

    Thanks for your article on this blog site. From my own experience, periodically softening upward a photograph might provide the photo shooter with a little an artsy flare. More often than not however, the soft clouds isn’t precisely what you had under consideration and can often times spoil an otherwise good photo, especially if you anticipate enlarging that.

    Bình luận
  43. custom mylar bags says:
    3 ngày ago

    Hello, you used to write wonderful, but the last several posts have been kinda boring? I miss your tremendous writings. Past several posts are just a bit out of track! come on!

    Bình luận
  44. 💵 BTC Compensation Ready to Withdraw ➜ telegra.ph/Compensations-03-29-2?hs=c762d84936d9ec6067ac7cb0c7b22f59& 💵 says:
    3 ngày ago

    frv1gf

    Bình luận
  45. ceramic implants says:
    3 ngày ago

    Thanks for discussing your ideas. I would also like to convey that video games have been actually evolving. Modern tools and inventions have aided create practical and active games. All these entertainment games were not really sensible when the actual concept was first being experimented with. Just like other kinds of technologies, video games also have had to progress as a result of many ages. This itself is testimony for the fast growth of video games.

    Bình luận
  46. Johnnyvat says:
    3 ngày ago

    автоответы Ozon Управление репутацией маркетплейс, подкрепленное автоматизированными системами ответов, формирует доверие покупателей и укрепляет позиции бренда в онлайн-среде.

    Bình luận
  47. Williamkef says:
    2 ngày ago

    кайт в хургаде кайт

    Bình luận
  48. casino-apps says:
    2 ngày ago

    Very nice post. I just stumbled upon your blog and wanted to say that I have really enjoyed browsing your blog posts. In any case I?ll be subscribing to your rss feed and I hope you write again soon!

    Bình luận
  49. beste casino zonder licentie says:
    1 ngày ago

    Thanks for your article. My partner and i have usually observed that a majority of people are needing to lose weight as they wish to appear slim and also attractive. Nevertheless, they do not generally realize that there are additional benefits to losing weight as well. Doctors state that over weight people are afflicted with a variety of disorders that can be perfectely attributed to their excess weight. The good news is that people who’re overweight and suffering from several diseases are able to reduce the severity of their own illnesses by way of losing weight. You are able to see a progressive but marked improvement in health whenever even a bit of a amount of weight reduction is achieved.

    Bình luận
  50. online casinos zonder licentie says:
    1 ngày ago

    Thanks for the tips on credit repair on this excellent site. What I would tell people is to give up the particular mentality they can buy right now and shell out later. As a society all of us tend to make this happen for many issues. This includes family vacations, furniture, along with items we’d like. However, it is advisable to separate your own wants from all the needs. When you are working to improve your credit rating score actually you need some sacrifices. For example you’ll be able to shop online to save cash or you can check out second hand shops instead of pricey department stores for clothing.

    Bình luận

Để lại một bình luận Hủy

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Recent News

  • Handling Vericlock Webhooks: Overcoming Challenges and Optimizing Costs
  • Webinoly – Trọn Bộ LEMP – Tối Ưu Hóa Máy Chủ Web Chạy NGINX
  • Upload Multiple Files và đính kèm vào Task sử dụng Teamwork API thông qua PHP Laravel API

Category

  • Aptech PHP
  • Command Line
  • Động Lực
  • Git
  • Khóa Học
  • Lập Trình Website
  • Laravel
  • Nginx
  • Phần Mềm
  • PHP
  • Software
  • SQL
  • Tiền Mã Hoá
  • Tuyển Dụng
  • WordPress

Thông Tin Website

  • Các Điều Khoản Và Điều Kiện
  • Chính Sách Bảo Mật
  • Liên Hệ

Liên Kết

  • Vape PHP Đà Nẵng Website
  • Nga Đức Ninh Thuận Website

Bản Quyền © 2018 - | Nam Còi | [email protected]. Thiết kế & Phát triển Web bởi TechYXE.

No Result
View All Result
  • Trang chủ
  • Lập Trình Website
  • Khóa Học
  • Động Lực
  • Tuyển Dụng

Bản Quyền © 2018 - | Nam Còi | [email protected]. Thiết kế & Phát triển Web bởi TechYXE.