Release 1.3.5 Jonathan Peoples, Colton Williams

64
dynamo Documentation Release 1.3.5 Jonathan Peoples, Colton Williams Dec 10, 2021

Transcript of Release 1.3.5 Jonathan Peoples, Colton Williams

Page 1: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo DocumentationRelease 1.3.5

Jonathan Peoples, Colton Williams

Dec 10, 2021

Page 2: Release 1.3.5 Jonathan Peoples, Colton Williams
Page 3: Release 1.3.5 Jonathan Peoples, Colton Williams

Dynamo Documentation

1 What is Dynamo? 3

2 The Dynamo Artisan Command 5

3 Quick-Start Video 73.1 The Dynamo Controller Backend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.2 The Dynamo Controller Frontend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.3 Installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.4 Creating Many-to-Many Relationships Between Dynamo Models . . . . . . . . . . . . . . . . . . . 153.5 Dynamo Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.6 Deleting Categories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463.7 Relating Models To Themselves with Dynamo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 483.8 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60

i

Page 4: Release 1.3.5 Jonathan Peoples, Colton Williams

ii

Page 5: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Dynamo Documentation 1

Page 6: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

2 Dynamo Documentation

Page 7: Release 1.3.5 Jonathan Peoples, Colton Williams

CHAPTER 1

What is Dynamo?

Dynamo is a you can use in your existing application to quickly build admins using real database tables. You maybe familiar with php artisan which allows you to create models, views, and controllers, and migration files with somescaffolding.

Dynamo does this, as well as generating the bootstrap index view and forms for creating the models. For an example,see the screenshots below showing a form on the backend of the website for the administrator to use to create Faqs.

This ^ is the index view of a module that shows a table of all the FAQs in the database.

3

Page 8: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

This ^ is the form view of a module that shows the form that the website administrator uses to create a new FAQ orupdate an existing FAQ.

4 Chapter 1. What is Dynamo?

Page 9: Release 1.3.5 Jonathan Peoples, Colton Williams

CHAPTER 2

The Dynamo Artisan Command

Dynamo allows you to create a complete controller, model, migration, and route for your backend admin module withone command:

php artisan make:dynamo Employee

After running this you will notice an autogenerated route in your routes->web.php file. You will notice a new databasemigration has been created in your database->migrations directory. You will notice a new model made in your appdirectory. You will notice a new Dynamo Controller in your controller directory. By default the controller directory isset in your dynamo config file in config->dynamo.php:

'controller_namespace' => 'App\Http\Controllers',

'controller_path' => app_path('/Http/Controllers'),

Need to opt out of some of the Dynamo magic?

php artisan make:dynamo Employee --migration=no --model=no --controller=no --route=no

I recommend starting out with the quick-start video below to see what Dynamo is capable of doing and seeing if it isright for you. If you see that it is right for you, you can install Dynamo by running:

composer require jzpeepz/dynamo

5

Page 10: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

6 Chapter 2. The Dynamo Artisan Command

Page 11: Release 1.3.5 Jonathan Peoples, Colton Williams

CHAPTER 3

Quick-Start Video

3.1 The Dynamo Controller Backend

This section isn’t necessary to know in order to use Dynamo. It is explaining encapsulated code within the packagethat you don’t actually need to see, but it might be helpful to understand what is going on when you create a modelwith Dynamo. Again, I would recommend watching the Quick Start video on the homepage of the documentation toget a better understanding.

The Dynamo Controller class contains functions similar to the functions of a Resource route in Laravel. Recall thatwhen you create a Dynamo object with the command,

php artisan make:dynamo FaqCategory

Dynamo will generate a route in your routes->web.php file,

Route::resource('faqcategory', '\App\Http\Controllers\Admin\FaqCategoryController');

that links to the Dynamo Controller. The following routes are generated in your Laravel application that you can seeby running php artisan route:list:

The index function returns an index view or table of all the resources for that module. So in the Faq Category example,the index function would show a view of all the Faq Categories saved in the database:

7

Page 12: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

public function index(){

$items = $this->dynamo->getIndexItems();

return DynamoView::make($this->dynamo, 'dynamo::index', compact('items'));}

Resulting DynamoView( screenshot is bootstrap3 theme instead of bootstrap4 ):

The next two functions are create() and store(). Create shows the form view that the user will use to create FaqCategory objects:

public function create(){

$item = new $this->dynamo->class;

$formOptions = ['route' => $this->dynamo->getRoute('store'),'files' => true,

];

return DynamoView::make($this->dynamo, 'dynamo::form', compact('item',→˓'formOptions'));}

Resulting DynamoView:

Store() is the function that gets hit when the user presses the submit button on the Create an Faq Category form. Storewill “store” this new Faq Category object in your database:

public function store(Request $request){

$item = new $this->dynamo->class;

$this->dynamo->store($item);

session(['alert-success' => $this->dynamo->getName() . ' was saved successfully!→˓']);

return redirect()->route($this->dynamo->getRoute('edit'), $item->id);}

8 Chapter 3. Quick-Start Video

Page 13: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Resulting DynamoView:

The next two functions are edit() and update() which go hand-in-hand the same way create() and store() go hand-in-hand. When the user clicks the edit button on one of the Faq Category objects in the index view, the form view forthat particular employee will be presented to the user so they can make changes to that Faq Category (perhaps updatea phone number, or in this case update the name of the category):

public function edit($id){

$className = $this->dynamo->class;

$item = $className::withoutGlobalScopes()->find($id);

$formOptions = ['route' => [$this->dynamo->getRoute('update'), $id],'method' => 'put','files' => true,

];

return DynamoView::make($this->dynamo, 'dynamo::form', compact('item',→˓'formOptions'));}

Update() gets hit when the user presses the Submit button and whatever changes they made will get updated for thatparticular Faq Category in the database:

public function update(Request $request, $id){

$className = $this->dynamo->class;

$item = $className::withoutGlobalScopes()->find($id);

$this->dynamo->store($item);

session(['alert-success' => $this->dynamo->getName() . ' was saved successfully!→˓']);

return redirect()->route($this->dynamo->getRoute('edit'), $id);}

The final function on the Dynamo Controller is destroy(). This function gets hit when the user clicks the delete buttonin the index view, and an alert will appear asking them if they are sure they want to do this. If they press yes, the itemwill attempt to be deleted. If the item can’t be deleted due to throwing a QueryException (because you can’t add orupdate a child row if another object in the database is using it for a foreign key), it will redirect and say you can’t dothat because this object is in use. Otherwise, the item will be deleted and they will be shown a success message, andthis Employee no longer exist:

public function destroy($id){

$className = $this->dynamo->class;

(continues on next page)

3.1. The Dynamo Controller Backend 9

Page 14: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

// Run through and look for fields with type 'multiSelect'foreach($this->dynamo->getFields() as $field) {

if($field->type == 'hasMany') {//if 'multiSelect' found then relational data may exist. Detach data from

→˓the model$className::withoutGlobalScopes()->find($id)->{$field->key}()->detach();

}

}

$item = $className::withoutGlobalScopes()->findOrFail($id);

try {$item->delete();

} catch (QueryException $e) {session(['alert-danger' => $this->dynamo->getName() . ' cannot be deleted

→˓while in use!']);return redirect()->route($this->dynamo->getRoute('index'));

}

session(['alert-warning' => $this->dynamo->getName() . ' was deleted successfully!→˓']);

return redirect()->route($this->dynamo->getRoute('index'));}

Resulting DynamoView if object can’t be deleted because it’s connected to other objects in the relational database viapivot tables:

Resulting DynamoView if object was successfully deleted:

10 Chapter 3. Quick-Start Video

Page 15: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.2 The Dynamo Controller Frontend

At this point in the workflow, you have:

1. Installed Dynamo into your Laravel app by running:

composer require jzpeepz/dynamo

2. Created an Object using:

php artisan make:dynamo FaqCategory

This command created a database migration, a model, and controller. Once you define the migration and run phpartisan migrate, you’re now ready for the third step:

3. Implement the Dynamo Controller!

Admin customization happens in your controller inside the getDynamo() function of the Dynamo Controller. Thisfunction returns a Dynamo instance which has lots of chainable methods that customize your Dynamo admin. Letsuse an Employee object for an example:

This is what your Dynamo Controller will look like by default right after it is created. By default the auto() function iscalled which will take all the things your employee object consist of (that you defined in your database migration, say

3.2. The Dynamo Controller Frontend 11

Page 16: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

first_name, last_name, phone_number) and create fields in the form for them. You can chain on methods to this autofunction if you need to do more specific things. It’s super easy! Check it out. . .

Everything you see before the comment “//ClearIndexes” will appear in the form view when someone is creating anobject in the database.

Example of Form View of a Dynamo admin/module:

12 Chapter 3. Quick-Start Video

Page 17: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Everything you see after the comment “//ClearIndexes” will appear in the index view when someone is viewing all theObjects in the database.

Example of Index View of a Dynamo admin/module:

Now, keep in mind, all the function calls you see above happened automatically with the auto() function. The onlything the auto function can’t do is stuff like renaming a field like you see happen at “->file(“photo”, “Headshot”)”where you might want it to say Headshot rather than photo. We will go into this in more detail in the DynamoMethods section.

This is the basics of Dynamo. You have created an admin on your custom CMS for managing Employees on yourLaravel application is less than 2 minutes. Isn’t that powerful? You could do this for News Post, Products being soldon the site, Faq’s and Faq Categories, and so on.

Note: NOTE: For the full list of chainable methods on your Dynamo Object, refer to section Dynamo Methods!

3.2. The Dynamo Controller Frontend 13

Page 18: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.3 Installation

Install via Composer:

composer require jzpeepz/dynamo

Include the service provider in your config/app.php:

Jzpeepz\Dynamo\DynamoServiceProvider::class

Publish the Dynamo config file:

php artisan vendor:publish --tag=dynamo

Note: NOTE: If using a local disk for uploading, be sure to symlink it to your public directory and provide the properpath in the config file.

If you’d like, the Github repo for Dynamo is .

You can edit the configuration of Dynamo at:

vendor->jzpeepz->dynamo->src->config->dynamo.php.

The vendor folder is a hidden folder, so much sure your text editor is letting you see it. jzpeeps is the root folder forthe Dynamo package.

images/config.png

Storage disk to use to store uploaded files.

Upload_path is the path within the storage disk to store the uploaded files. This is also the directory within the publicdirectory to which the storage directory is linked.

route_prefix is a prefix to add to all Dynamo routes.

layout is the layout to use with Dynamo views.

controller_namespace and controller_path is the path that tells Dynamo where you want you’re controllers to be auto-generated.

view_prefix does this. . . TODO

view_theme is the HTML / CSS framework you are using. Dynamo is build to run on either ‘bootstrap3’ or ‘boot-strap4’.

target_blade_section does this. . . TODO

default_has_many_class does this . . . TODO

model_uses, just like the comment says, is used to generate use statements automatically into the Model class that isgenerated when you make a Dynamo object.

model_implements, similar to model_uses, tells Dynamo what interfaces you want your Dynamo objects to implement.

Finally, model_traits tells Dynamo what traits the object should use.

14 Chapter 3. Quick-Start Video

Page 19: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

See an example of a auto generated Dynamo object below that uses these last three configurations to generate modelsthat use a Media Manager package from Spatie.

3.4 Creating Many-to-Many Relationships Between Dynamo Models

php artisan make:dynamo Faqphp artisan make:dynamo Category

Example Faq migration:

Schema::create('faqs', function (Blueprint $table) {$table->increments('id');$table->string('question', 255);$table->mediumText('answer');$table->timestamps();

});

Example Category migration:

Schema::create('categories', function (Blueprint $table) {$table->increments('id');$table->string('name');$table->timestamps();

});

Example pivot table migration:

3.4. Creating Many-to-Many Relationships Between Dynamo Models 15

Page 20: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Schema::create('category_faq', function(Blueprint $table) {$table->integer('faq_id')->unsigned()->nullable();$table->foreign('faq_id')->references('id')->on('faqs');

$table->integer('category_id')->unsigned()->nullable();$table->foreign('category_id')->references('id')->on('categories');

});

Run:

php artisan migrate

For the Category model:

public function faqs(){

return $this->belongsToMany('App\Faq');}

For the Faq Model:

public function categories(){

return $this->belongsToMany('App\Category');}

return Dynamo::make(\App\Employee::class)->hasMany('categories', ['options' => [$categories]]);

Note: NOTE: You can see a full example of this process in the next section, Dynamo Methods, on the hasManySimplefunction

3.5 Dynamo Methods

This section will list all available methods that you are able to chain onto your Dynamo object that you create insideyour Dynamo Controller. For a very simple admin, you might be able to get away with only using that auto() methodwhich is auto-generated for you, and literally have no work to do. But in the case of a database relationship, or thecase of renaming a field in the form, or sizing a picture a specific way, etc, you need to use the methods below.

Note: This list is alphabetical order, and you can quickly jump to a function description by clicking the links above

->select('status', ['attributes' => ['disabled' => true, 'colton' => ''],'options' => Testing::getStatuses(),'label' => 'Your Address Please sir','tooltip' => 'Use the \'\'Draft\'\' status to save information as you have it.

→˓When you\'re ready for an FAQ toshow up on the front end of the website, change it to \'\

→˓'Published\'\' and then click the \'\'Save FAQ\'\' button.','position' => 200,

(continues on next page)

16 Chapter 3. Quick-Start Video

Page 21: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

'required' => true,])

->addActionButton(function() {return '<a href="/pilot/testing" class="btn btn-primary btn-sm">Test</a>';

})

Here we see the code, simply chain the function onto your Dynamo object in your Dynamo controller. addActionBut-ton() takes one parameter which should be a closure function that returns raw html for a link and bootstraps buttonclasses. You can return any raw html you want; it doesn’t have to be bootstrap, you could just use the button html tag.

Result:

3.5. Dynamo Methods 17

Page 22: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Path to addField() partials.

->addFilter('term', Term::orderBy('name', 'desc')->get()->prepend(['id' => '', 'name'→˓=> 'All'])->pluck('name', 'id'), function ($query) {

$termId = request()->input('term');

if (empty($termId)) {return $query;

}

return $query->select('representatives.*')->distinct()->join('representative_term', 'representative_term.representative_id

→˓', '=', 'id')->where('representative_term.term_id', $termId);

(continues on next page)

18 Chapter 3. Quick-Start Video

Page 23: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

});

Here is where we called addFilter on the DynamoController. The parameters are the database field you want tofilter by(in this case terms), a collection of the objects(in this case, we grabbed all terms names and sorted them indescending order), then a closure function that actually does the filtering. In this case, depending on what term youchoose, we will grab all the Representatives from that term.

Filtered by Term 2222

Filtered by term 2016

Here I’ve called many different functions on this dynamo form, but we only interested in the bottom right-hand corner.I’ve called ->hideDelete() and ->addFormFooterButton() to “overwrite” the delete button with my own delete buttonthat does something extra you’ll see in the next screenshot. I also called ->addFormFooterButton() a second time toget that “LOL” button that you, and it links to whatever page I want it to within my website/application.

->addFormFooterButton(function() {return '<a href="/pilot/testing" class="btn btn-warning btn">LOL</a>';

})->addFormFooterButton(function() {

return '<a href="" class="btn btn-danger btn" data-toggle="modal" data-target="→˓#relationships-manager-modal">Delete</a>';

(continues on next page)

3.5. Dynamo Methods 19

Page 24: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

})->hideDelete()

Here you see how I override the delete button. I use my ->addFormFooterButton function to create a Bootstrap 4button that has data-toggle=”modal” data-target=”#relationships-manager-modal” included. This makes this deletebutton open up a Bootstrap 4 modal when clicked. This modal is built into Dynamo so all you have to do is overwritethe delete button. The modal that pops up allows the user to delete the category they are currently editing. See the nextscreenshot for what the modal looks like.

Here you see the Bootstrap modal that pops up after clicking the delete button. If the user types the name of thecategory and checks the box they can delete the category. Form Footer Buttons are awesome!

Here you can see I called ->addFormHeaderButton() twice to create a bootstrap4 primary button with the text “HAHA”and a bootstrap4 warning button with the text “LOL”. Of course, you can make these buttons link to anywhere in yourwebsite/application. See the code below

->addFormHeaderButton(function() {return '<a href="/pilot/testing" class="btn btn-primary btn-sm">HAHA</a>';

})->addFormHeaderButton(function() {

return '<a href="/pilot/testing" class="btn btn-warning btn-sm">LOL</a>';})

20 Chapter 3. Quick-Start Video

Page 25: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->select('status', ['attributes' => ['disabled' => true, 'colton' => ''],'options' => Testing::getStatuses(),'label' => 'Your Address Please sir','tooltip' => 'Use the \'\'Draft\'\' status to save information as you have it.

→˓When you\'re ready for an FAQ toshow up on the front end of the website, change it to \'\

→˓'Published\'\' and then click the \'\'Save FAQ\'\' button.','position' => 200,

])

(TBD)

->clearIndexes()->addIndexButton(function () {

return '<a href="/pilot/representative/import" class="btn btn-primary btn-xs">→˓Import Representatives from Spreadsheet</a>';})->addIndex('headshot', 'Photo', function ($item) {

(continues on next page)

3.5. Dynamo Methods 21

Page 26: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

if (empty($item->headshot_small)) {return '';

}return '<img style="width: 100px " src="' .$item->headshot_small. '" class=""

→˓style="width: 60px;">';})->addIndex('name')->addIndex('district')->searchable('first_name')->searchable('last_name')->indexOrderBy('last_name')

In the closure function, we are checking to see if this Representative has a Headshot photo in the database or not. Ifnot, just display an empty string. If so, display their image with a certain width.

The first three Representatives did not have pictures, the fourth did.

->clearIndexes()->addIndex('title')->addIndex('short_description')->addIndex('activate', 'Active', function ($item) {

//return $item->activate ? '<h3><span class="label label-success">Yes</span></h3>→˓' : '<h3><span class="label label-danger">No</span></h3>';

return $item->activate ? '<i class="far fa-check-circle fa-3x" style="color:→˓green; padding-top: 10px;"></i>' :

'<i class="far fa-times-circle fa-3x" style="color: red; padding-top: 10px;"></i>→˓';})

The third addIndex uses a closure that uses a ternary operation to check if this Alert is activated. If so, a success boxis rendered with the word “Yes” in it. If not, a danger box is rendered with the word “No” in it.

22 Chapter 3. Quick-Start Video

Page 27: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Here, in the top right corner, we add a button in the Representatives Manager that says Import Representatives fromSpreadsheet that links to another form for uploading Representatives via .csv.

This is the page the button links to

->addIndexButton(function () {return '<a href="/pilot/representative/import" class="btn btn-primary btn-xs">

→˓Import Representatives from Spreadsheet</a>';})

3.5. Dynamo Methods 23

Page 28: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

This is the function in use. It takes one parameter that is a closure function that returns raw html linking to that page.

class FaqController extends DynamoController{

public function getDynamo(){

return Dynamo::make(\App\Faq::class)->auto()

}}

Auto function being called on the newly created Dynamo object.

24 Chapter 3. Quick-Start Video

Page 29: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

The form that auto() produces for the Faq object.

3.5. Dynamo Methods 25

Page 30: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

The index view auto() produces for Faqs.

->auto()->checkbox('activate', [

'label' => 'Activate: Only one may be active at a time'])

//set admin view->clearIndexes()->addIndex('title')->addIndex('short_description')->addIndex('activate', 'Active', function ($item) {

//return $item->activate ? '<h3><span class="label label-success">Yes</span></h3>→˓' : '<h3><span class="label label-danger">No</span></h3>';

return $item->activate ? '<i class="far fa-check-circle fa-3x" style="color:→˓green; padding-top: 10px;"></i>' :

'<i class="far fa-times-circle fa-3x" style="color: red; padding-top: 10px;"></i>→˓';})->indexOrderBy('title');

A checkbox method is called here, with an array of options containing one option, ‘label’, so let the user know thatthey can only activate one Alert at a time.

26 Chapter 3. Quick-Start Video

Page 31: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

The result on the form for Alerts.

The result on the index view for Alerts.

I’ve commented out my addIndex() calls for the sake of demonstration. The next image shows the result.

3.5. Dynamo Methods 27

Page 32: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Now I’ve uncommented my addIndex calls to show the result in the next image.

->auto()->file('photo')->textarea('bio', [

'class' => 'wysiwyg-editor',])->text('job_title_extra', [

'label' => 'Additional Job Title (optional)',])->hasManySimple('departments')->removeField('position')

//Set indexes for admin view->clearIndexes()->addIndex('photo', 'Photo', function ($item) {

if (empty($item->photo)) {return '';

}return '<img style="width: 100px " src="' .$item->photo. '" class="" style=

→˓"width: 60px;">';})->addIndex('first_name')->addIndex('last_name')->addIndex('departments', "Departments", function($item){

return $item->departments->implode('name', ', ');

})

->indexOrderBy('last_name');

Notice the file method call.

28 Chapter 3. Quick-Start Video

Page 33: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

This is the result for the form view. The user can select the photo from their computer.

This is the result of the index view.

->select('status', ['attributes' => ['disabled' => true, 'colton' => ''],'options' => Testing::getStatuses(),'label' => 'Your Address Please sir','tooltip' => 'Use the \'\'Draft\'\' status to save information as you have it.

→˓When you\'re ready for an FAQ toshow up on the front end of the website, change it to \'\

→˓'Published\'\' and then click the \'\'Save FAQ\'\' button.','position' => 200,

])

(TBD)

3.5. Dynamo Methods 29

Page 34: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->select('status', ['attributes' => ['disabled' => true, 'colton' => ''],'options' => Testing::getStatuses(),'label' => 'Your Address Please sir','tooltip' => 'Use the \'\'Draft\'\' status to save information as you have it.

→˓When you\'re ready for an FAQ toshow up on the front end of the website, change it to \'\

→˓'Published\'\' and then click the \'\'Save FAQ\'\' button.','position' => 200,

])

(TBD)

30 Chapter 3. Quick-Start Video

Page 35: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Schema::create('faqs', function (Blueprint $table) {$table->increments('id');$table->string('question');$table->string('short_answer');$table->string('long_answer');$table->string('faq_categories');$table->timestamps();

});

Schema::create('faq_categories', function (Blueprint $table) {$table->increments('id');$table->string('name');$table->timestamps();

});

Schema::create('faq_faq_category', function (Blueprint $table) {$table->integer('faq_id')->unsigned();$table->foreign('faq_id')->references('id')->on('faqs');

$table->integer('faq_category_id')->unsigned();(continues on next page)

3.5. Dynamo Methods 31

Page 36: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

$table->foreign('faq_category_id')->references('id')->on('faq_categories');});

First ^^ , make sure you have created the relationship your in database migrations.

*This function goes on the Faq model*public function faq_categories(){

return $this->belongsToMany('App\FaqCategory');}

*This function goes on the FaqCategory model*public function faqs(){

return $this->belongsToMany('App\Faq');}

Next ^^ , make sure both your models have a public function that relates the two.

->auto()->text('question')->text('short_answer')->textarea('answer', [

'class' => 'wysiwyg-editor',])->hasManySimple('faq_categories')

// setup the index view->clearIndexes()

->addIndex('question')

->addIndex('short_answer');

Now ^^ , on the controller, you can call hasManySimple() and the first parameter should be named EXACTLY theway you named the functions on the model in the previous steps. ( which is faq_categories )

32 Chapter 3. Quick-Start Video

Page 37: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

This is the result on the form. The user is able to select many categories for each FAQ they make.

And when they submit the form, your database will create the relationship between this FAQ_id and that FAQ Cate-gory_id.

->auto();//hideAdd()

First I comment out hideAdd() to show the default.

Notice that the add FAQ Category button exist in the top right corner of the container by default.

->auto()hideAdd();

Now I uncomment hideAdd(), . . .

3.5. Dynamo Methods 33

Page 38: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Now the FAQ Category button isn’t available to the user so they can’t create new FAQ Categories.

->auto();//hideDelete()

First I just took a basic DynamoController and commented out the hideDelete() function to show the default.

Notice you have an Edit/Delete button by default under your Action index

->auto()hideDelete();

Now I uncomment hideDelete(), . . .

And the delete button is hidden. Magical isn’t it?

->ignoredScopes(['deleted_at', 'age_scope'])->applyScopes()->removeField('position')

//set admin view->clearIndexes()->addIndexButton(function () {

return '<a href="/pilot/staff/import" class="btn btn-primary btn-xs">Import Staff→˓from Spreadsheet</a>';})->addIndex('hamburger', 'Sort', function($item) {

return '<i class="fas fa-bars fa-2x" ></i>';})->addIndex('name')->addIndex('job_title')->indexOrderBy('position');

->auto()->removeField('position')

//set admin view->clearIndexes()->addIndexButton(function () {

return '<a href="/pilot/staff/import" class="btn btn-primary btn-xs">Import Staff→˓from Spreadsheet</a>';})->addIndex('hamburger', 'Sort', function($item) {

return '<i class="fas fa-bars fa-2x" ></i>';})->addIndex('name')

(continues on next page)

34 Chapter 3. Quick-Start Video

Page 39: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

->addIndex('job_title')->indexOrderBy('position');

In this case, rather than indexOrderBy(‘last_name’), we made it where the user could drag-and-drop the staff membersin the order they would like in the index view. Wherever they dropped the Staff member, it would update that staffmembers position in the database. Then we can just indexOrderBy(‘position’).

->paginate(10)

I call paginate and pass in 10 so I get 10 items per page. See the screenshot below. We have 10 FAQs on the first pageand links to the next pages below the table

3.5. Dynamo Methods 35

Page 40: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->password()

TBD . . .

36 Chapter 3. Quick-Start Video

Page 41: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->auto()->removeField('position')

//set admin view->clearIndexes()

(continues on next page)

3.5. Dynamo Methods 37

Page 42: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

->addIndexButton(function () {return '<a href="/pilot/staff/import" class="btn btn-primary btn-xs">Import Staff

→˓from Spreadsheet</a>';})->addIndex('hamburger', 'Sort', function($item) {

return '<i class="fas fa-bars fa-2x" ></i>';})->addIndex('name')->addIndex('job_title')->indexOrderBy('position');

See how we remove the position field in the form. We don’t want the user to have to fill that out in the form becausethey are able to drag-and-drop staff members to set the position in the index view.

->searchable('first_name')->searchable('last_name')

Here we call searchable twice for first and last name.

Here we see you can search by last_name

38 Chapter 3. Quick-Start Video

Page 43: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Here we see you can search by first_name

Here we see search working for first and last name at the same time.

->select('faq_category_id', ['options' => FaqCategory::all()->prepend(['name' => '', 'id' => ''])->pluck('name',

→˓ 'id'),'label' => 'Faq Category (optional)','position' => 150,

])->select('resource_category_id', [

'options' => ResourceCategory::all()->prepend(['name' => '', 'id' => ''])->pluck(→˓'name', 'id'),

'label' => 'Resource Category (optional)','position' => 160,

])->select('event_tag_id', [

'options' => Tag::all()->prepend(['name' => '', 'id' => ''])->pluck('name', 'id'),'label' => 'Events Tag Category (optional)','position' => 170,

])->select('post_tag_id', [

'options' => Tag::all()->prepend(['name' => '', 'id' => ''])->pluck('name', 'id'),'label' => 'News Tag (optional)','position' => 180,

(continues on next page)

3.5. Dynamo Methods 39

Page 44: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

])->select('status', [

'options' => Program::getStatuses(),'position' => 190,

])

Notice that we have three selects on this Program DynamoController. The user may optionally select categories toconnect to this program they are creating. This way, on the front-end of the website, they will see FAQ’s related to thisprogram in a sidebar when they are on this programs page.

This is the form view that the user will interact with.

How the select boxes options look. (little bug here with the blank spaces, don’t worry about that)

40 Chapter 3. Quick-Start Video

Page 45: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->setAddItemText('SET ADD TEXT')

Here I call the function ->setAddItemText() to override the default Add Item Text. The next screenshot shows theresult.

This is the result. Notice in the upper right-hand corner the text of the green “Add” button has changed.

->setFormPanelTitle("Form boy")

Here I call the function ->setFormPanelTitle() to override the default Form Panel Title Text. The next screenshotshows the result.

->setIndexPanelTitle("Colton's module manger override text")

Here I call the function ->setIndexPanelTitle() to override the default Index Panel Title Text. The next screenshotshows the result.

3.5. Dynamo Methods 41

Page 46: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->setSaveItemText('SET SAVE TEXT')

Here I call the function to override the Save button text on the form.

->auto()->text('this is a text box, my friend')

The simplest example.

42 Chapter 3. Quick-Start Video

Page 47: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

The result on the form. ^

->auto()->text('district', [

'position' => 0])->singleImage('headshot', [

'maxWidth' => 400])->singleImage('featured_image', [

'maxWidth' => 1600])->gallery('gallery')->text('party', [

'position' => 50])->text('email', [

'position' => 60])->text('phone', [

'position' => 60])->text('fax', [

'position' => 70])->text('address', [

'position' => 80])->text('city', [

'position' => 90])->text('zip', [

'position' => 100])->text('seniority', [

'position' => 110(continues on next page)

3.5. Dynamo Methods 43

Page 48: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

])->text('religion', [

'position' => 120])->text('past_service', [

'position' => 130])

In this example, we pass in an array of options with only one option, that being ‘position’. The position option is thereso you can manually set the order of the fields in the form if needed. The auto() function usually handles this, but insome cases you may want to reorder.

A more complicated example. Here we pass in an option to the top two text fields. This option is ‘class’ => ‘date-TimePicker’ which lets a little calendar pop up to aid the user in selected the dates for the these fields. And I’m noteven going to explain what’s going on in the third text field. It was a super weird case, usually things don’t look thatmessy.

44 Chapter 3. Quick-Start Video

Page 49: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Here we see the dateTimePicker

3.5. Dynamo Methods 45

Page 50: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

->textarea('bio', ['class' => 'wysiwyg-editor',

])

3.6 Deleting Categories

We’ve added a new feature in Dynamo where you can override the default delete button in the index view to make abootstrap modal popup and give you the ability to delete an entire category at once. MySQL will not allow you todelete data from a table if that data is being used in a pivot table. This is likely true for other database types as well.In the Laravel framework, you can go to config->database to see what type of database your application is using. Atour company, we use MySQl and Sequel Pro for all of our projects. Not sure if this feature will work or not if you’renot using MySQL in your project.

Anyway, below you can click the images to see an example. For the demonstration, I’ve created two Dynamo modelsand controllers with the php artisan make:dynamo command. The first is Faq, and the second is FaqCategory. Afterrunning these two commands to create these models, I write these database migration files and connect the two througha pivot table. After that, write the relationship on the model files, and then I implement the Dynamo Controllers witha ->hasManySimple() function and voila; we’ve given the user the ability to create Faqs for their website and attachthem to categories. . . But what if at some point they want to delete a category? Before this feature, they would haveto manually go detach all the Faqs from the category before the system would let them delete the category becauseMySQL will not let you “add or update a child row: a foreign key constraint fails”.

Taken from the official MySQL documentation:

46 Chapter 3. Quick-Start Video

Page 51: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Note: “Foreign key relationships involve a parent table that holds the central data values, and a child table withidentical values pointing back to its parent. The FOREIGN KEY clause is specified in the child table. It will rejectany INSERT or UPDATE operation that attempts to create a foreign key value in a child table if there is no a matchingcandidate key value in the parent table.”

php artisan make:dynamo Faqphp artisan make:dynamo FaqCategory

Schema::create('faqs', function (Blueprint $table) {$table->increments('id');$table->string('name');$table->string('image');$table->text('long_description');$table->boolean('activated');$table->integer('status');$table->timestamps();

});

Schema::create('faq_categories', function (Blueprint $table) {$table->increments('id');$table->string('name');$table->timestamps();

});

Schema::create('faq_faq_category', function (Blueprint $table) {$table->integer('faq_id')->unsigned();$table->foreign('faq_id')->references('id')->on('faqs');

$table->integer('faq_category_id')->unsigned();$table->foreign('faq_category_id')->references('id')->on('faq_categories');

});

This function goes on the Faq.php file in the root of the app folder

public function faq_categories(){

return $this->belongsToMany('App\FaqCategory');}

This function goes on the FaqCategory.php file in the root of the app folder

public function faqs(){

return $this->belongsToMany('App\Faq')->orderBy('name');}

3.6. Deleting Categories 47

Page 52: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7 Relating Models To Themselves with Dynamo

Sometimes you will run into the case where you want to relate a model to itself. One of our clients wanted theirweb-app’s product detail page to list other products in a “related_products” relationship. Or maybe you’re building asocial network and you want to relate Users to Users in a relationship called “friends”.

The issue that came about with Dynamo is that we used the ->hasManySimple(‘related_products’) method on the Dy-namoController. The pivot table called “product_product” had two columns “product_id” and “related_product_id”.When creating a product you could use the multiSelect box to select the related products, but if you saved and thenwent to that related product, it wouldn’t show the original product in the multiSelect box, and thus, the user mightassume that no related products were attached, and attach some related products. If they attached the same productagain you would end up with entries in the pivot table like (1,2) and (2,1). We wanted there to only be one entry (1,2)and the application understand on each of those product details pages, to list each other as related. Below is how weimplemented this:

First we created the following relationships:

public function getAllRelatedProductsAttribute($value){

return $this->allRelatedProducts();}

public function allRelatedProducts(){

return $this->relatedProducts->merge($this->relatedTo);}

public function relatedProducts(){

return $this->belongsToMany('App\Product', 'product_product', 'product_id',→˓'related_product_id');}

public function relatedTo(){

(continues on next page)

48 Chapter 3. Quick-Start Video

Page 53: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

return $this->belongsToMany('App\Product', 'product_product', 'related_product_id→˓', 'product_id');}

Let’s start from the bottom up. All of these relationships were defined on the Product.php model in the root of the appdirectory in the Laravel framework. relatedTo() says that “this” Product model is relatedTo itself (AppProduct), thepivot table’s name is “product_product”, and we set the “this” Product to the related_product_id column and set theproduct that we are relating it to to the product_id column. If this sounds a bit confusing please refer in the Laraveldocumentation to how these parameters work. Next we need a relationship function that returns all the products thatare related to “this” Product; so we reverse the third and fourth parameter. Next, we make a relationship function calledallRelatedProducts which merges the relatedTo() and relatedProduts() function so the collection will contain both oftheir collections in one. Then we make a getter function that returns the allRelatedProducts() relationship function.

->formTab(FormTab::make('Relationships')->select('allRelatedProducts', [

'label' => 'Related Products: Select all products related→˓to this product',

'options' => Product::orderBy('name')->get()->pluck('name→˓', 'id'),

'class' => 'chosen-select','multiple' => true,

])->select('product_category_id', [

'label' => 'Product Category: Select the category for→˓this product',

'options' => ProductCategory::getSelectList(),'class' => 'chosen-select',

])->select('product_type_id', [

'label' => 'Product Type: Select the type for this product→˓',

'options' => ProductType::getSelectList(),'class' => 'chosen-select',

])->select('sales_sheet_id', [

'label' => 'Sales Sheet: Select the sales sheet for this→˓product',

'options' => ResourceCategory::getSalesSheetSelectList(),'class' => 'chosen-select',

])->select('warranty_id', [

'label' => 'Warranty: Select the warranty for this product→˓',

'options' => ResourceCategory::getWarrantySelectList(),'class' => 'chosen-select',

])->select('drawing_id', [

'label' => 'Drawing: Select the drawing for this product','options' => ResourceCategory::getDrawingSelectList(),'class' => 'chosen-select',

])->hasManySimple('resources', [

'label' => 'Resources: Select all resources for this→˓product',

'options' => Resource::orderBy('title')->get()->pluck(→˓'title', 'id'),

(continues on next page)

3.7. Relating Models To Themselves with Dynamo 49

Page 54: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

(continued from previous page)

'nameField' => 'title',])

)

We create this FormTab called Relationships where the user may attach all the relationships this Product they arecreating has. Look at the first select box. The first parameter is ‘allRelatedProducts’. This is needed so that if the useris Editing an already existing Product that already has related Products set, it will populate with those related Products.Another important thing is that it has ‘multiple’ => true,’ which tells dynamo to make it a multiSelect box on the form.Directly below this formTab we create a handler:

->addHandler('allRelatedProducts', function (&$product, &$data) {if (isset($data['allRelatedProducts'])) {

$relatedIds = $product->allRelatedProducts->pluck('id');

$relatedIdsToRemove = array_diff($relatedIds->toArray(),→˓$data['allRelatedProducts']);

$product->relatedTo()->detach($relatedIdsToRemove);

$product->relatedProducts()->sync($data[→˓'allRelatedProducts']);

unset($data['allRelatedProducts']);} else {

// clear both relationships$product->relatedProducts()->detach();$product->relatedTo()->detach();

}})

The first parameter is the relationship that we are handling. The second is a closure function that does the “handling”.It takes the item and its data that we are creating/editing as the closure arguments. It’s important that you at the ‘&’ topass the arguments by reference because we need the data outside the scope of the function. Then we do an if statementthat says, if the Product has allRelatedProducts data already set, then get the products relatedProduct ids, and find thedifference in the ids that were submitted. Detach the leftover ids, because that means the user deselected Productsthat were once related but now they don’t want them related anymore. Then “sync” or update the relatedProducts()relationship. Look here for the method in the documentation. Then unset the part of the data.

else, if the allRelatedProducts select box is not set to anything (blank, user either cleared all related products or therewasn’t any to begin with) then detach all related products both ways.

All that’s pretty much it! it’s admittedly a bit confusing at first. But this algorithm is what needs to be done anytimeyou have a model that relates to itself. For example, in the case of creating a “friends” relationship between two Usermodels, you would create relationships functions “friendsOf”, “friends”, “allFriends”, and the getter function. Thencreate the select box on the “allFriends” relationship. And the handler code is exactly the same, except you need torename the variables and relationship function names appropriately and it will work.

Then in the form when creating a new User, the admin can select the friends of that User. If then, the admin saves,and takes a look at one of those friend models, the new User will populate in the select box automatically. If therelationship gets detached on either model, it will automatically be detached from the other model as well, since thepivot table is saving it in one row rather than like I explained at the beginning.

Below are some screenshots of the outcome (The captions under them help explain whats going on):

50 Chapter 3. Quick-Start Video

Page 55: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7. Relating Models To Themselves with Dynamo 51

Page 56: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Here I create a new Product

52 Chapter 3. Quick-Start Video

Page 57: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7. Relating Models To Themselves with Dynamo 53

Page 58: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

I relate my new Product to another Product called Related Product.

Here we see the pivot table and 1 single row related these two products.

54 Chapter 3. Quick-Start Video

Page 59: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7. Relating Models To Themselves with Dynamo 55

Page 60: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Next I navigate to and Edit the Related Product in the CMS.

56 Chapter 3. Quick-Start Video

Page 61: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7. Relating Models To Themselves with Dynamo 57

Page 62: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

And I automatically see the new Product I created attached to it. If we were using a ->hasManySimple() function onour DynamoController like we normally would to relate two SEPARATE models (not the same model), we would notsee the newly created Product show up here automatically, and the user may try to select it again, creating a duplicatein the database which is not good.

58 Chapter 3. Quick-Start Video

Page 63: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

3.7. Relating Models To Themselves with Dynamo 59

Page 64: Release 1.3.5 Jonathan Peoples, Colton Williams

dynamo Documentation, Release 1.3.5

Still on the Related Product Edit page, I deselect the Product1 I created from the beginning and hit save. We expectthat the row in the database is gone, and that if I go back to Product1 and edit it, that I won’t see Related Productattached to it.

As expected, the row is gone and the two Products are no longer related.

3.8 License

The MIT License

Copyright 2018 Jonathan Peoples/Flex360

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documen-tation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use,copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whomthe Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of theSoftware.

THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PAR-TICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHTHOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTIONOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFT-WARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Question? Please contact [email protected]

60 Chapter 3. Quick-Start Video