lazos99

lazos99

Web Developer at Darkpony Ltd

Member Since 4 Years Ago

Limassol

Experience Points
4,980
Total
Experience

20 experience to go until the next level!

In case you were wondering, you earn Laracasts experience when you:

  • Complete a lesson — 100pts
  • Create a forum thread — 50pts
  • Reply to a thread — 10pts
  • Leave a reply that is liked — 50pts
  • Receive a "Best Reply" award — 500pts
Lessons Completed
42
Lessons
Completed
Best Reply Awards
0
Best Reply
Awards
  • start your engines Created with Sketch.

    Start Your Engines

    Earned once you have completed your first Laracasts lesson.

  • first-thousand Created with Sketch.

    First Thousand

    Earned once you have earned your first 1000 experience points.

  • 1-year Created with Sketch.

    One Year Member

    Earned when you have been with Laracasts for 1 year.

  • 2-years Created with Sketch.

    Two Year Member

    Earned when you have been with Laracasts for 2 years.

  • 3-years Created with Sketch.

    Three Year Member

    Earned when you have been with Laracasts for 3 years.

  • 4-years Created with Sketch.

    Four Year Member

    Earned when you have been with Laracasts for 4 years.

  • 5-years Created with Sketch.

    Five Year Member

    Earned when you have been with Laracasts for 5 years.

  • school-in-session Created with Sketch.

    School In Session

    Earned when at least one Laracasts series has been fully completed.

  • welcome-newcomer Created with Sketch.

    Welcome To The Community

    Earned after your first post on the Laracasts forum.

  • full-time-student Created with Sketch.

    Full Time Learner

    Earned once 100 Laracasts lessons have been completed.

  • pay-it-forward Created with Sketch.

    Pay It Forward

    Earned once you receive your first "Best Reply" award on the Laracasts forum.

  • subscriber Created with Sketch.

    Subscriber

    Earned if you are a paying Laracasts subscriber.

  • lifer Created with Sketch.

    Lifer

    Earned if you have a lifetime subscription to Laracasts.

  • evangelist Created with Sketch.

    Laracasts Evangelist

    Earned if you share a link to Laracasts on social media. Please email [email protected] with your username and post URL to be awarded this badge.

  • chatty-cathy Created with Sketch.

    Chatty Cathy

    Earned once you have achieved 500 forum replies.

  • lara-veteran Created with Sketch.

    Laracasts Veteran

    Earned once your experience points passes 100,000.

  • 10k-strong Created with Sketch.

    Ten Thousand Strong

    Earned once your experience points hits 10,000.

  • lara-master Created with Sketch.

    Laracasts Master

    Earned once 1000 Laracasts lessons have been completed.

  • laracasts-tutor Created with Sketch.

    Laracasts Tutor

    Earned once your "Best Reply" award count is 100 or more.

  • laracasts-sensei Created with Sketch.

    Laracasts Sensei

    Earned once your experience points passes 1 million.

  • top-50 Created with Sketch.

    Top 50

    Earned once your experience points ranks in the top 50 of all Laracasts users.

Level 1
4,980 XP
Nov
22
6 days ago
Activity icon

Replied to How Do You Handle Controllers With Partial Identical Business Logic?

@martinbean

I will be most specific why I am not really sure for the adapter pattern and then you can tell me your opinion.

The way we use LDAP is to basically sync the users to the DB. Index, restore, destroy, export actions they use identical business logic no matter if the user came AD or Eloquent because at the end the user is stored to a table using eloquent.

The only time something is different is when I am creating/storing a user. When the client wants to use Eloquent I display him a form that he can add first name, last name, email, password etc... but when they use AD I just have a dual list box with the list of users from AD that the user can select and import to the application. So in terms of business logic the Ldap part most of the actions are 70% the identical logic from Eloquent and only the create part is a bit different (has different logic).

Therefore what I ended up doing is have an abstract UserService to have the identical business logic and then EloquentUserService and LdapUserService inherit that one and they have the business logic that is not the same.

Activity icon

Replied to How Do You Handle Controllers With Partial Identical Business Logic?

@rodrigo.pedra

Your reply did help a lot.

My repository example was simply a way to display what I had in mind but actually I was never gonna use repositories. My application interacts with MSSQL and that will not change any time soon. On the internet and some Laracasts videos they use repositories in a way to be able to swap storage but as you said other people use repositories for other stuff.

I will use "Services" instead but I will do my best so that these classes do not become another God object like my controllers. Right now I am just trying to decouple my code, make my controllers thinner and use some of these patterns like that adapter pattern to improve my way of writing my applications.

One issue I have with the existing application is the issue with the abstract controllers and the declarations I mentioned on my first post. I will avoid inheritance for now on and I will try to change my existing code.

I really like the "actions" idea but that ship has already sailed I think... I will use them for another application for sure.

@martinbean

I know it looks like you can use the adapter pattern but I am not really sure.

Nov
21
1 week ago
Activity icon

Replied to How Do You Handle Controllers With Partial Identical Business Logic?

@rodrigo.pedra

Thank you for your reply.

Regarding the indexDataProvider I agree with you I just was not sure whether is a good or bad idea to use the abstract controller like that. Even though if you have an alternative idea I would like to hear it. I do not mind rewriting the whole code. I want to learn :).

For the (store, update, destroy) I am not sure I understood your approach...

If I understood it correctly, instead of putting the store, update, restore on the abstract controller it's rather better to have the actions on both controllers (EloquentUserController and LdapUserController) and use a repository for the shared logic.

For example (I can put the repository on the constructor I left it for easy reading):

LdapUserController

/**
     * Remove the specified resource from storage.
     *
     * @param $id
     * @return mixed
     * @throws \Exception
     */
    public function destroy(UserRepository $repository, $id)
    {
       $repository->destroy($id);

	//Return or redirect...
    }

/**
     * Restore a user given an id
     *
     * @param Request $request
     * @return mixed
     */
    public function restore(UserRepository $repository, $id)
    {
        $repository->restore($id);
	
	//Return or redirect...
    }

EloquentUserController

/**
     * Remove the specified resource from storage.
     *
     * @param $id
     * @return mixed
     * @throws \Exception
     */
    public function destroy(UserRepository $repository, $id)
    {
       $repository->destroy($id);

	//Return or redirect...
    }

/**
     * Restore a user given an id
     *
     * @param UserRepository $repository
     * @return mixed
     */
    public function restore(UserRepository $repository, $id)
    {
        $repository->restore($id);

	//Return or redirect...
    }
Activity icon

Started a new Conversation How Do You Handle Controllers With Partial Identical Business Logic?

I have an application that has a user management page and our customers have the choice to either use Eloquent or LDAP (Active Directory) to manage the users.

For that I have 2 Controllers EloquentUserController and LdapUserController and an abstract class UserController for the share/identical logic.

The business logic to display the list of users is identical except the loaded view (view.eloquent.index and view.ldap.index).

The business logic to export, restore, destroy a user is the same no matter if it's eloquent or LDAP and I have it ALL on the abstract controller.

The business logic to edit and create is very different because Ldap you have to sync the user information from AD that why they or on a separated controller.

For the index action my code looks like this:

Index on LdapUserController

public function index(LdapRequest $request)
    {
        return view('user::pages.users.adldap.index', parent::index($request));
    }

Index on EloquentUserController

public function index(EloquentRequest $request)
    {
        return view('user::pages.users.eloquent.index', parent::index($request));
    }

Index on Abstract UserController

public function index(Request $request)
    {
	//some business logic...

        return compact('users', 'roles');
}

The issue now from the samples above is that I want to inject on the index actions a Custom Request class that has different logic between Eloquent and Ldap. If I add a generic Request class on the abstract controller PHP is complaining that the "declaration should be compatible with..."

What do you do in such cases? Do you give the abstract a different name? Using abstract controllers like this is a bad idea and you do something else?

Nov
20
1 week ago
Activity icon

Awarded Best Reply on PHPUnit Runs Only One Test.

I finally figured it out with --debug. The 2nd test actually never ended. According to PHP Error logs I had some helpers that did not have if (! function_exists('function_name')) { therefore PHP was re-declaring them causing a Fatal Error.

Activity icon

Replied to PHPUnit Runs Only One Test.

I finally figured it out with --debug. The 2nd test actually never ended. According to PHP Error logs I had some helpers that did not have if (! function_exists('function_name')) { therefore PHP was re-declaring them causing a Fatal Error.

Activity icon

Started a new Conversation PHPUnit Runs Only One Test.

Hi,

I am trying to test a sample class and and when I run vendor/bin/phpunit it only runs one test and stops.

For example for the below it just outputs a dot (.) that according to the documentation it pass the tests. But if you look at the second method it expects to be true but I set it to false and it stills outputs a dot.

Is like PHP Unit runs only the first method and it stops. What I am doing wrong?

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->assertTrue(true);
    }

    /**
     * @test
     */
    public function testFirstTest()
    {
        $this->assertTrue(false);
    }

    /**
     * @test
     */
    public function testSecondTest()
    {
        $this->assertTrue(true);
    }

    /**
     * @test
     */
    public function testThirdTest()
    {
        $this->assertTrue(true);
    }
}

This is my phpunit.xml

<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
         backupStaticAttributes="false"
         bootstrap="vendor/autoload.php"
         colors="true"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         processIsolation="false"
         stopOnFailure="false">
    <testsuites>
        <testsuite name="Feature">
            <directory suffix="Test.php">./tests/Feature</directory>
        </testsuite>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
        <testsuite name="Modules">
            <directory suffix="Test.php">./Modules/**/Tests</directory>
        </testsuite>
        <testsuite name="Modules-Feature">
            <directory suffix="Test.php">./Modules/**/Tests/Feature</directory>
        </testsuite>
        <testsuite name="Modules-Unit">
            <directory suffix="Test.php">./Modules/**/Tests/Unit</directory>
        </testsuite>
    </testsuites>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./app</directory>
			<directory suffix=".php">./modules</directory>
            <exclude>
                <directory suffix="blade.php">./modules</directory>
                <directory suffix=".php">./modules/**/Routes</directory>
                <directory suffix=".php">./modules/**/Resources</directory>
                <directory suffix=".php">./modules/**/Tests</directory>
                <directory suffix=".php">./modules/**/Config</directory>
            </exclude>
        </whitelist>
    </filter>
    <php>
        <env name="APP_ENV" value="testing"/>
        <env name="BCRYPT_ROUNDS" value="4"/>
        <env name="CACHE_DRIVER" value="array"/>
        <env name="SESSION_DRIVER" value="array"/>
        <env name="QUEUE_DRIVER" value="sync"/>
    </php>
</phpunit>
Nov
11
2 weeks ago
Activity icon

Replied to Service Layer

Taking into consideration your comments at the end everything depends on the application you develop and how "big" the application is.

At the end the solution is to create small classes and divide your code accordingly.

Activity icon

Replied to Service Layer

@laracoft

For the example, I provided it looks like that is a generic data handler to store, update, and retrieve from a model... The application is modular and this is the settings module and in general this is what it does in most cases.

But I have other modules that have more business logic... for example the below logic is inside a controller of another module. The methods $this->countRejectedItems, $this->countItemsWithoutAmount etc... are protected methods inside the controller. This is why I started with the service layer and the SOLID princibles, because the controller should not be responsible for any of those methods.

This method in the controller based on your criteria is not readable and not scalable.

Do not you agree? Therefore is better to move this logic to a service class or something.

/**
     * Change the status of the batch to processed.
     *
     * @param BatchSecurity $security
     * @param $id
     * @return mixed
     */
    public function processed(BatchSecurity $security, $id)
    {
        if(!$batch = Batch::with('session')->withTrashed()->find($id)) {
            return redirect()->route($this->process->getRoute('reconciliation.index'))->withErrorMsg(trans('reconciliation::batches.messages.error.batch_id', ['id' => $id]));
        }

        if(!$security->check($batch, $this->process)->run()) {
            return $security->redirect();
        }

        if($batch->trashed()) {
            $batch->update(['in_use_by' => null, 'status' => Batch::STATUS_DELETED]);
            return redirect()->route($this->process->getRoute('reconciliation.session.show'), $batch->session->id);
        }

        //Do we have rejected documents?
        $rejected = $this->countRejectedItems($batch);
        //Do we have documents without amount?
        $empty_amounts = $this->countItemsWithoutAmount($batch);
        //Is the batch amount not equal with the documents total amount?
        $amount_difference = $batch->amountDifference();
        //Is the batch number of documents not equal with number total number of documents?
        $items_difference = $batch->itemsDifference();

        $batch->in_use_by = null;

        if($rejected != 0 || $empty_amounts != 0 || $amount_difference != 0 || $items_difference != 0) {
            $batch->status = Batch::STATUS_UNPROCESSED;
            if($batch->save()) {
                return redirect()->route($this->process->getRoute('reconciliation.session.show'), $batch->session->id)->withInfoMsg(trans('reconciliation::batches.messages.info.unprocessed_batch'));
            } else {
                return redirect()->route($this->process->getRoute('reconciliation.session.show'), $batch->session->id)->withErrorMsg(trans('reconciliation::batches.messages.error.processed_status'));
            }
        }

        $batch->status = Batch::STATUS_PROCESSED;

        if($batch->save()) {
            return redirect()->route($this->process->getRoute('reconciliation.session.show'), $batch->session->id)->withSuccessMsg(trans('reconciliation::batches.messages.success.processed_status'));
        } else {
            return redirect()->route($this->process->getRoute('reconciliation.session.show'), $batch->session->id)->withErrorMsg(trans('reconciliation::batches.messages.error.processed_status'));
        }
    }
Activity icon

Replied to Service Layer

@laracoft

I agree with you. I will keep the more "beefy" code into the service. Something like below. Do you agree?

class FieldService extends Service
{
    /**
     * Get the paginated fields.
     *
     * @param RequestFilter $request
     * @return LengthAwarePaginator
     */
    public function getPaginatedFields(RequestFilter $request)
    {
        $filter = $request->filter();

        $query = $this->fields($filter);

        $total = $query->count();

        $items = $query->skip($filter->get('filter_limit') * ($filter->get('filter_page') - 1))
            ->take($filter->get('filter_limit'))
            ->orderBy('fields.name', 'ASC')
            ->get();

        return new LengthAwarePaginator($items->all(), $total, $filter->get('filter_limit'));
    }

    /**
     * Get the the fields based on the filtered criteria if any
     *
     * @param Filter $filter
     * @return Builder
     */
    protected function fields(Filter $filter)
    {
        $query = Field::query()->with('dbtables');

        if ($filter->get('filter_search')) {
            $query->where(function ($query) use ($filter) {
                $query->where('name', 'LIKE', '%'.$filter->get('filter_search').'%');
                $query->orWhere('title', 'LIKE', '%'.$filter->get('filter_search').'%');
                $query->orWhere('description', 'LIKE', '%'.$filter->get('filter_search').'%');
            });
        }

        if ($filter->get('filter_table')) {
            $query->whereHas('dbtables', function ($query) use ($filter) {
                $query->where('db_table_id', $filter->get('filter_table'));
            });
        }

        if ($filter->get('filter_type')) {
            $query->where('type', $filter->get('filter_type'));
        }

        if ($filter->get('filter_status') != '') {
            $query->where('status', $filter->get('filter_status'));
        }

        return $query;
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Request $request
     *
     * @return false|Field
     */
    public function store(Request $request)
    {
        $field = new Field;
        $field->name = Str::slug($request->input('name'), '_');
        $field->type = $request->input('type');
        $field->default_value = $request->input('default_value');
        $field->is_nullable = ($request->has('is_nullable')) ? 1 : 0;
        $field->is_queryable = ($request->has('is_queryable')) ? 1 : 0;
        $field->queryable_label = $request->input('queryable_label');
        $field->title = $request->input('title');
        $field->description = $request->input('description');
        $field->is_custom = ($request->has('is_custom')) ? 1 : 0;
        $field->status = ($request->has('status')) ? 1 : 0;

        if ($field->save()) {
            $this->handleSchema($request->input('json-schema'), $field->id);
            return $field;
        } else {
            return false;
        }
    }

    /**
     * Update the specified resource in storage.
     *
     * @param Field $field
     * @param Request $request
     *
     * @return false|Field
     */
    public function update(Field $field, Request $request)
    {
        $field->title = $request->input('title');
        $field->queryable_label = $request->input('queryable_label');
        $field->description = $request->input('description');
        $field->is_custom = ($request->has('is_custom')) ? 1 : 0;
        $field->is_queryable = ($request->has('is_queryable')) ? 1 : 0;
        $field->status = ($request->has('status')) ? 1 : 0;

        if ($field->save()) {
            $this->handleSchema($request->input('json-schema'), $field->id);
            return $field;
        } else {
            return false;
        }
    }
}
Activity icon

Replied to Service Layer

@laracoft

No impact at all. I was trying to keep everything related to the Field model into the service class to have everything more organise.

In my head I had this in mind: Let's say I have the Controllers A, B and C. All 3 controllers at some point they have to call Field:find($id). Instead of doing the Eloquent way maybe it's better to just inject a service and use it like this $fieldService->find($id) on all 3 controllers.

Activity icon

Replied to Service Layer

Hi, thanks for your reply. To be honest I prefer the service class approach... I just do not know whether making a service method just to find, pluck, and get all fields are necessary or just use the Eloquent way on the controller is bad practice.

I read on another article that SOLID principles are not something that you must follow strictly it will help managing your application in ther future.

Activity icon

Replied to Service Layer

@laracoft Hi, I am already using CRUD controllers. The reason that I used the service is that on some of the controller methods I am checking if the field exists (Field::find($id)) otherwise return back with an error. That Field::find($id) was repeated multiple times therefore it was moved to the service.

Maybe I am over thinking and over complicating things that why I asked if I use on the controller Field::find($id) repeatedly it's not a big deal.

class FieldsController extends Controller
{
    /**
     * The field service.
     *
     * @var FieldService $fieldService
     */
    protected $fieldService;

    /**
     * The DB Table service.
     *
     * @var DbTableService $dbTableService
     */
    protected $dbTableService;

    public function __construct(FieldService $fieldService, DbTableService $dbTableService)
    {
        $this->fieldService = $fieldService;

        $this->dbTableService = $dbTableService;
    }

    /**
     * Display a listing of the resource.
     *
     * @param Index $request
     * @return View
     */
    public function index(Index $request)
    {
        $fields = $this->fieldService->getPaginatedFields($request);

        $data = [
            'title' => trans('setting::fields.index_title'),
            'fields' => $fields->withPath(route('fields.index')),
            'dbtables' => DbTable::pluck('name', 'id')->toArray()
        ];

        return view('setting::pages.fields.index', $data);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return View
     */
    public function create()
    {
        $data = [
            'title' => trans('setting::fields.add_title'),
            'schema' => $this->fieldService->getSchema(),
            'dbtables' => DbTable::pluck('name','id')->toArray()
        ];

        return view('setting::pages.fields.create', $data);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param Create $request
     *
     * @return RedirectResponse
     */
    public function store(Create $request)
    {
        if ($field = $this->fieldService->store($request)) {
            return redirect()->route('fields.edit', $field->id)
                ->withSuccessMsg(trans('setting::fields.messages.success.create'));
        } else {
            return redirect()->route('fields.create')
                ->withErrorMsg(trans('setting::fields.messages.error.create'));
        }
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param int $id
     *
     * @return View
     */
    public function edit($id)
    {
        if (!$field = $this->fieldService->find($id)) {
            return redirect()->route('fields.index')
                ->withErrorMsg(trans('setting::fields.messages.error.id', ['id' => $id]));
        }

        $data = [
            'title' => trans('setting::fields.edit_title'),
            'schema' => $this->fieldService->getSchema($id),
            'field' => $field,
            'dbtables' => DbTable::pluck('name','id')->toArray()
        ];

        return view('setting::pages.fields.edit', $data);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param Edit $request
     * @param int $id
     *
     * @return RedirectResponse
     */
    public function update(Edit $request, $id)
    {
        if (!$field = $this->fieldService->find($id)) {
            return redirect()->route('fields.index')
                ->withErrorMsg(trans('setting::fields.messages.error.id', ['id' => $id]));
        }

        if ($this->fieldService->update($field, $request)) {
            return redirect()->route('fields.edit', $field->id)
                ->withSuccessMsg(trans('setting::fields.messages.success.edit'));
        } else {
            return redirect()->route('fields.edit', $field->id)
                ->withErrorMsg(trans('setting::fields.messages.error.edit'));
        }
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param int $id
     *
     * @return RedirectResponse
     */
    public function destroy($id)
    {
        if (!$field = $this->fieldService->find($id)) {
            return redirect()->route('fields.index')
                ->withErrorMsg(trans('setting::fields.messages.error.id', ['id' => $id]));
        }

        if ($this->fieldService->destroy($field)) {
            return redirect()->route('fields.index')
                ->withSuccessMsg(trans('setting::fields.messages.success.delete'));
        } else {
            return redirect()->route('fields.index')
                ->withWarningMsg(trans('setting::fields.messages.warning.delete', ['name' => $field->name]));
        }
    }
}
Activity icon

Started a new Conversation Service Layer

I have been reading recently regarding SOLID, DRY Controllers, Repositories etc...

Most articles suggest the use of a Service Layer. Therefore I started moving the business logic away from my controllers but then I felt that I am creating a wrapper that at some points is not necessary.

For example I started creating methods like the following:

class FieldService extends Service
{
    /**
     * Pluck the columns from the model.
     *
     * @param $value
     * @param null $key
     *
     * @return mixed
     */
    public function pluck($value, $key = null)
    {
        return Field::pluck($value, $key);
    }

    /**
     * Find a field.
     *
     * @param $id
     *
     * @return Field|false
     */
    public function find($id)
    {
        return Field::find($id);
    }

    /**
     * Get all fields.
     *
     * @return Collection
     */
    public function all()
    {
        return Field::all();
    }
}

I started using the service and I realised that sometimes I am using pluck with ordering. With the new service I cannot do anymore Field::orderBy('column','ASC')->pluck('column','key'); because the service as is returns the results and then I have to use other methods to do the ordering when I prefer for the DB to do such a simple task.

The field service does not have just these simple methods it contains also complex business logic. Is it OK if I leave these simple methods like Field::find($id) or Field::pluck() or Field::all() outside from the Service class and just use Eloquent as is on the controller?

The project I am working on, it will always use MSSQL there is no reason for me to do any repositories.