{"id":18772471,"url":"https://github.com/eftec/example-php-editablegrid","last_synced_at":"2025-09-01T21:30:45.052Z","repository":{"id":71041633,"uuid":"261019773","full_name":"EFTEC/example-php-editablegrid","owner":"EFTEC","description":"It is a library","archived":false,"fork":false,"pushed_at":"2020-05-04T00:07:24.000Z","size":304,"stargazers_count":5,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-07T19:36:12.466Z","etag":null,"topics":["editable-grid","editable-table","php"],"latest_commit_sha":null,"homepage":"https://www.eftec.cl","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EFTEC.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-03T20:59:22.000Z","updated_at":"2024-09-23T21:04:00.000Z","dependencies_parsed_at":"2023-06-30T00:45:10.790Z","dependency_job_id":null,"html_url":"https://github.com/EFTEC/example-php-editablegrid","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2Fexample-php-editablegrid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2Fexample-php-editablegrid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2Fexample-php-editablegrid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EFTEC%2Fexample-php-editablegrid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EFTEC","download_url":"https://codeload.github.com/EFTEC/example-php-editablegrid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231713132,"owners_count":18415195,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["editable-grid","editable-table","php"],"created_at":"2024-11-07T19:29:11.758Z","updated_at":"2024-12-29T08:13:44.694Z","avatar_url":"https://github.com/EFTEC.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Example PHP Inline Editable Grid with MySQL\nIt is an example of an inline editable grid, using PHP and MySQL\n\n![](docs/final.jpg)\n\nThis example uses jQuery, Ajax and a library called Gijgo ( [https://github.com/atatanasov/gijgo](https://github.com/atatanasov/gijgo)  All of them are for free)\n\n\n\n## 1- Configuring Composer.\n\nFor this exercise, we need composer installed and running (also PHP and MySQL)\n\nInitialize composer as follow.\n\n\n\n```shell\ncomposer init\nWelcome to the Composer config generator\n\nThis command will guide you through creating your composer.json config.\n\nPackage name (\u003cvendor\u003e/\u003cname\u003e) [jorge/example-php-editablegrid]: eftec/example-php-editablegrid\nDescription []: It is an tutorial to create an editable grid with php and mysql\nMinimum Stability []:\nPackage Type (e.g. library, project, metapackage, composer-plugin) []: project\nLicense []: MIT\n\nDefine your dependencies.\n\nWould you like to define your dependencies (require) interactively [yes]?\nSearch for a package: eftec/bladeone\nEnter the version constraint to require (or leave blank to use the latest version):\nUsing version ^3.43 for eftec/bladeone\nSearch for a package: eftec/pdoone\nEnter the version constraint to require (or leave blank to use the latest version):\nUsing version ^1.37 for eftec/pdoone\nSearch for a package:\nWould you like to define your dev dependencies (require-dev) interactively [yes]? no\n\nDo you confirm generation [yes]?\nWould you like the vendor directory added to your .gitignore [yes]?\nWould you like to install dependencies now [yes]?\nLoading composer repositories with package information\nUpdating dependencies (including require-dev)\nPackage operations: 2 installs, 0 updates, 0 removals\n\n  - Installing eftec/bladeone (3.43): Loading from cache\n  - Installing eftec/pdoone (1.37): Loading from cache\n    eftec/bladeone suggests installing eftec/bladeonehtml (Extension to create forms)\n    eftec/pdoone suggests installing eftec/validationone (For keeping and storing the messages)\n    Writing lock file\n    Generating autoload files\n\n\n```\n\nWe will also add the next libraries **[eftec/bladeone](https://www.github.com/eftec/bladeone)**  and **[eftec/pdoone](https://www.github.com/eftec/pdoone)**.  (for template and database)\n\n\n\n## 2 Creating tables\n\nWe need to create two tables on MySQL. The case of the column matters.   \n\n### Country\n\nVia code\n\n```sql\nCREATE TABLE country (\n  `IdCounty` INT NOT NULL AUTO_INCREMENT,\n  `Name` VARCHAR(45) NULL,\n  PRIMARY KEY (`IdCounty`));\n  \n```\n\nOr using an IDE\n\n![](docs/country_table.jpg)\n\n### Table players\n\nUsing script\n\n```sql\nCREATE TABLE `players` (\n  `ID` INT NOT NULL AUTO_INCREMENT,\n  `Name` VARCHAR(50) NULL,\n  `IdCounty` INT NULL,\n  `IsActive` INT NULL,\n  PRIMARY KEY (`ID`),\n  INDEX `players_fk_idx` (`IdCounty` ASC) VISIBLE,\n  CONSTRAINT `players_fk`\n    FOREIGN KEY (`IdCounty`)\n    REFERENCES `country` (`IdCounty`)\n    ON DELETE NO ACTION\n    ON UPDATE NO ACTION);\n    \n    \n```\n\n![](docs/players_table.jpg)\n\n\n\nAnd a foreign key (table players)\n\n![](docs/players_fk.jpg)\n\n\n\n## 3 Adding Data to the table\n\n### Data to Country\n\n```sql\nINSERT INTO `country` (`IdCounty`, `Name`) VALUES ('1', 'USA');\nINSERT INTO `country` (`IdCounty`, `Name`) VALUES ('2', 'Canada');\nINSERT INTO `country` (`IdCounty`, `Name`) VALUES ('3', 'Mexico');\nINSERT INTO `country` (`IdCounty`, `Name`) VALUES ('4', 'Portugal');\n\n```\n\n\n\n### Data to Players\n\n```sql\nINSERT INTO `players` (`ID`, `Name`, `IdCounty`, `IsActive`) VALUES ('1', 'Bryce Harper', '1', '1');\nINSERT INTO `players` (`ID`, `Name`, `IdCounty`, `IsActive`) VALUES ('2', 'Manny Machado', '2', '0');\nINSERT INTO `players` (`ID`, `Name`, `IdCounty`, `IsActive`) VALUES ('3', 'Carlos Correa', '1', '1');\nINSERT INTO `players` (`ID`, `Name`, `IdCounty`, `IsActive`) VALUES ('4', 'Francisco Lindor,', '3', '1');\n```\n\n## 4 Creating the Repository Layer\n\nThe repository layer is classes that have functions to access the database, for example, insert,update, etc.\n\nThe library PdoOne allows to create the repository layer using the database.\n\nLet's create this PHP file (in the root of the project)\n\n📄/genrepo.php\n\n```php\n\u003c?php\n\nuse eftec\\PdoOne;\n\ninclude \"vendor/autoload.php\";\n\n$pdo=new PdoOne('mysql','127.0.0.1','root','abc.123','example_editable_grid');\n$pdo-\u003elogLevel=3;\n$pdo-\u003erender();\n```\n\nwhere 127.0.0.1 is the server, root = user, abc.123 = password and example_editable_grid our base.\n\n**loglevel**=3 is if something goes south then it will show the error.\n\nAnd renders shows the next screen: (open the page to show this page)\n\n![](docs/ide.jpg)\n\n\n\nAdd the next information.  Where it says \"**input**\", select the name of the table to generate (**country** or **players**)\n\nFor output, selects the option \"**classcode**\". It will generate our class.\n\nPress generate and copy the result (is generated in **log** text area)\n\nIt will generate the next code\n\n```php\n\u003c?php\n/** @noinspection PhpUnused */\n\nuse eftec\\PdoOne;\nuse eftec\\_BasePdoOneRepo;\n\n\nclass CountryRepo extends _BasePdoOneRepo\n{\n   const TABLE = 'country';\n   // .... more code\n\n}\n```\n\nCopy this code and save in a folder (in my case I will use the folder **repo**)\n\nRepeats the same procedure with all the tables.\n\n📄 repo/CountryRepo.php\n\n📄 repo/PlayersRepo.php\n\n## 5 Views\n\nIn the folder views, create the next file 📄 views/table.blade.php\n\nIt also uses a web service called 📁 ws/server.php **that it does not exists yet but we will create it later.**\n\n 📄 views/table.blade.php\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003ejQuery Grid Inline Editing\u003c/title\u003e\n    \u003cmeta charset=\"utf-8\" /\u003e\n    \u003clink rel=\"stylesheet\" href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css\" integrity=\"sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO\" crossorigin=\"anonymous\"\u003e\n    \u003cscript src=\"https://code.jquery.com/jquery-3.3.1.min.js\"\u003e\u003c/script\u003e\n    \u003cscript src=\"https://unpkg.com/gijgo@1.9.13/js/gijgo.js\" type=\"text/javascript\"\u003e\u003c/script\u003e\n    \u003clink href=\"https://unpkg.com/gijgo@1.9.13/css/gijgo.css\" rel=\"stylesheet\" type=\"text/css\" /\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv class=\"container-fluid\"\u003e\n    \u003cbutton id=\"btnAdd\" class=\"gj-button-md\"\u003eAdd Row\u003c/button\u003e\n    \u003cdiv class=\"row\"\u003e\n        \u003cdiv class=\"col-xs-12\"\u003e\n            \u003ctable id=\"grid\"\u003e\u003c/table\u003e\n        \u003c/div\u003e\n    \u003c/div\u003e\n\u003c/div\u003e\n\u003cscript type=\"text/javascript\"\u003e\n    $(document).ready(function () {\n        let grid, countries;\n        grid = $('#grid').grid({\n            dataSource: 'ws/server.php?action=get',\n            uiLibrary: 'bootstrap4',\n            primaryKey: 'ID',\n            inlineEditing: { mode: 'command' },\n            columns: [\n                { field: 'ID', width: 44 },\n                { field: 'Name', editor: true },\n                { field: 'CountryName', title: 'Nationality', type: 'dropdown', editField: 'IdCounty'\n                    , editor: { dataSource: 'ws/server.php?action=countries', valueField: 'IdCounty', textField:'Name' } },\n                { field: 'IsActive', title: 'Active?', type: 'checkbox', editor: true, width: 90, align: 'center' }\n            ],\n            pager: { limit: 5 }\n        });\n        grid.on('rowDataChanged', function (e, id, record) {\n            // Clone the record in new object where you can format the data to format that is supported by the backend.\n            const data = $.extend(true, {}, record);\n            // Format the date to format that is supported by the backend.\n\n            // Post the data to the server\n            $.ajax({ url: 'ws/server.php?action=save', data: { record: data }, method: 'POST' })\n                .done(function (e) {\n                    if(e.result===false) {\n                        alert(e.message);\n                    }\n                })\n                .fail(function () {\n                    alert('Failed to save.');\n                });\n        });\n        grid.on('rowRemoving', function (e, $row, id, record) {\n            if (confirm('Are you sure?')) {\n                $.ajax({ url: 'ws/server.php?action=delete', data: { id: id }, method: 'POST' })\n                    .done(function (e) {\n                        if(e.result===false) {\n                            alert(e.message);\n                        }\n                        grid.reload();\n                    })\n                    .fail(function () {\n                        alert('Failed to delete.');\n                    });\n            }\n        });\n        $(\"#btnAdd\").on(\"click\",function() {\n            $.ajax({url: 'ws/server.php?action=add', data: {}, method: 'POST'})\n                .done(function (e) {\n                    if(e.result===false) {\n                        alert(e.message);\n                    }\n                    grid.reload();\n                })                \n                .fail(function () {\n                    alert('Failed to insert.');\n                })\n        });\n    });\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n## 6 Our webpage\n\nIt is our code page. It doesn't do much (yet) but later you could add some code. This code will call our view (created in the previous step)\n\n 📄 /table.php\n\n```php\n\u003c?php\n\nuse eftec\\bladeone\\BladeOne;\n\ninclude \"vendor/autoload.php\";\n\n$blade=new BladeOne();\n\n\necho $blade-\u003erun('table',[]);\n```\n\n## 7 And let's run\n\n\n\n![](docs/notfound.jpg)\n\n![](docs/meh.jpg)\n\n\n\nOk, it is a start but it misses the web services mentioned in the step 5.\n\n## 8 Creating web service.\n\nCreate a new PHP file called 📄 ws/server.php with the next content.\n\n📄 ws/server.php\n\n```php\n\u003c?php /** @noinspection ForgottenDebugOutputInspection */\n\nuse eftec\\PdoOne;\n\nheader('Content-Type: application/json');\n\ninclude '../vendor/autoload.php';\ninclude '../repo/CountryRepo.php';\ninclude '../repo/PlayersRepo.php';\n\n$pdoOne=new PdoOne('mysql','127.0.0.1','root','abc.123','example_editable_grid');\n$pdoOne-\u003elogLevel=3;\ntry {\n    $pdoOne-\u003econnect();\n} catch (Exception $e) {\n    var_dump($e-\u003egetMessage());\n    die(1);\n}\n\n$action=@$_GET['action'];\n\n//$countries=CountryRepo::toList();\n//var_dump($countries);\n\nswitch ($action) {\n    case 'add':\n        $record=PlayersRepo::factoryNull(); // we create an empty array\n    \n        try {\n            PlayersRepo::insert($record);\n            $result=['result'=\u003etrue];\n        } catch (Exception $e) {\n            $result=['result'=\u003efalse,'message'=\u003e$e-\u003egetMessage()];\n        }\n        echo json_encode($result);\n        break;\n    case 'save':\n        $record=$_POST['record'];\n        unset($record['CountryName']); // we delete the field countryname\n        $record['IsActive']=($record['IsActive'])?1:0; // we convert isactive(boolean) to 1 or 0.\n        try {\n            PlayersRepo::update($record);\n            $result=['result'=\u003etrue];\n        } catch (Exception $e) {\n            $result=['result'=\u003efalse,'message'=\u003e$e-\u003egetMessage()];\n        }\n        \n        echo json_encode($result);\n        break;\n    case 'delete':\n        $id=$_POST['id'];\n        try {\n            PlayersRepo::deleteById($id);\n            $result=['result'=\u003etrue];\n        } catch (Exception $e) {\n            $result=['result'=\u003efalse,'message'=\u003e$e-\u003egetMessage()];\n        }\n        \n        echo json_encode($result);\n        break;\n    case 'get':\n        $r=[];\n        try {\n            $r['records'] = $pdoOne-\u003eselect('ID,Players.Name,Country.Name as CountryName,IsActive')-\u003efrom('Players')\n                                   -\u003eleft('Country on Players.IdCounty=Country.IdCounty')-\u003etoList();\n        } catch (Exception $e) {\n            $result=['result'=\u003efalse,'message'=\u003e$e-\u003egetMessage()];\n        }\n        $r['total']=count($r['records']);\n        echo json_encode($r);\n        break;\n    case 'countries':\n        try {\n            $result = CountryRepo::toList();\n        } catch (Exception $e) {\n            $result=['result'=\u003efalse,'message'=\u003e$e-\u003egetMessage()];\n        }\n        echo json_encode($result);\n        break;\n    default:\n        echo json_encode(['result'=\u003efalse,'message'=\u003e'???? '.$action]);\n}\n```\n\nWhat it does? I will explain:\n\n### 8.1 Connecting to the database\n\nThe first part of the code, connects to the database. In error, it dumps the error.  You should change the information of the database such as user, password, etc.\n\n```php\n$pdoOne=new PdoOne('mysql','127.0.0.1','root','abc.123','example_editable_grid');\n$pdoOne-\u003elogLevel=3;\ntry {\n    $pdoOne-\u003econnect();\n} catch (Exception $e) {\n    var_dump($e-\u003egetMessage());\n    die(1);\n}\n$action=@$_GET['action'];\n```\n\nIt also reads the argument of the URL called action, i.e. **ws/server.php?action=somevalue**\n\n### 8.2 Inserting\n\nFor inserting, we create an empty array and we use the class **PlayersRepo** (created automatically)\n\n```php\n$record=PlayersRepo::factoryNull();\nPlayersRepo::insert($record);\n```\n\n### 8.3 Listing\n\nListing the countries. We use the class **CountryRepo**. We could also sort, paging and filtering.\n\n```php\n$result = CountryRepo::toList(); // select * from country\n```\n\nListing the players. We need a custom query so we create it directly (without the Repository class). It is because we need all the columns of players but also the name of the country.\n\n```php\n$r['records'] = $pdoOne-\u003eselect('ID,Players.Name,Country.Name as CountryName,IsActive')-\u003efrom('Players')\n                               -\u003eleft('Country on Players.IdCounty=Country.IdCounty')-\u003etoList();\n```\n### 8.4 Update and delete\n\nWe update with an array with all the columns. For delete, we only need the primary key.\n\n```php\nPlayersRepo::update($record);\n\nPlayersRepo::deleteById($id);\n```\n\n## 9 Let's run\n\nExecute the same page than step 7 and it is the final result. If something fails, then you could check the error with your browser (KEY F12)\n\n![](docs/final.jpg)\n\n## 10 Final step\n\n:-)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feftec%2Fexample-php-editablegrid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feftec%2Fexample-php-editablegrid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feftec%2Fexample-php-editablegrid/lists"}