I was wondering what would be the best practice to create the following tables and relations in eloquent / laravel 4.1 models.
table name magazines with fields id, title
table name magazine_issues with fields id, title, magazine_id
table name magazine_issue_articles with fields id, title, body, magazine_issue_id
I need the file/dir structure and the naming of all the Class and files, because I am really confused, for example should I make a directory named magazines and place the Issue.php file there and like so for the articles? And what should be the name of the class itself?
Thank you in advance.
EDIT
I need to have them in subfolders, since that would be much more portable. The question is...
will a class with the name magazineIssue (with table name magazine_issues) will be autoloaded and registered from a file called Issue.php in a directory named Magazine/?
I hope you are not as confused as I am :(
If you want to create thinks like Taylor Otwell and 'the core' is trying to teach people do things in Laravel, this is a good start:
Your files could be organized as
├── app
│ ├── Publishing
│ │ ├── Models
│ │ │ ├── Magazine
│ │ │ │ ├── Magazine.php
│ │ │ │ ├── Issue.php
│ │ │ │ ├── Article.php
│ │ ├── BaseModel.php
│ │ ├── Repositories
│ │ │ ├── MagazineRepository.php
Configure a PSR-0 or PSR-4 (better) to autoload your classes:
"psr-0": {
"Publishing": "app/"
},
Create your namespaced BaseModel:
<?php namespace Publishing\Models
use Eloquent;
class BaseModel extends Eloquent {
}
Create namespaced models, according to your source tree:
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Magazine extends BaseModel {
protected $table = 'magazines';
}
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Issue extends BaseModel {
protected $table = 'magazines_issues';
}
<?php namespace Publishing\Models\Magazine
use Publishing\Models\BaseModel;
class Article extends BaseModel {
protected $table = 'magazines_issues_articles';
}
You also may want to create a MagazineRepository class, to ease the access to your Magazine Domain:
<?php namespace Publishing\Repositories;
use Publishing\Models\Magazine\Magazine;
use Publishing\Models\Magazine\Issue;
use Publishing\Models\Magazine\Article;
class MagazineRepository implements DataRepositoryInterface {
private $state;
public function __construct(
Magazine $magazine,
Issue $issue,
Article $article
)
{
$this->magazine = $magazine;
$this->issue = $issue;
$this->article = $article;
}
public function getAll()
{
return $this->magazine->all();
}
public function findArticle($articleId)
{
return $this->article->find($articleId);
}
public function getByArticle($articleId)
{
return $this->findArticle($articleId)->magazine;
}
}
First off, remember that your tables should all be plural - magazine_issues not magazine_issue, magazine_issue_articles not magazine_issue_article.
As for directory structure, with Laravel it's kinda a do-what-you-want. A lot of people try to use namespaces and put models usually in a Models sub-namespace.
That said, your models should* match your tables names but converting the plural snake_case to singular StudlyCaps. So you should end up with three models:
class Magazine extends Eloquent
{
public function issues()
{
return $this->hasMany('MagazineIssue');
}
}
class MagazineIssue extends Eloquent
{
public function magazine()
{
return $this->belongsTo('Magazine');
}
public function articles()
{
return $this->hasMany('MagazineIssueArticle');
}
}
class MagazineIssueArticle extends Eloquent
{
public function issue()
{
return $this->belongsTo('MagazineIssue');
}
}
However, where you put them is still up to you. Just ensure that you use fully-qualified class names in your relationships if you mess round with namespaces. I'd suggest they all go in the same directory and namespace, though.
* I say you should do all this, but you are free to do what you want. The reason for the 'should' is that if you follow these conventions, Laravel will make your life a little easier.
Before going deeper into DB and table, you should have you domain layer organized around Laravel model. In your case, since you are working with issues, the name of your model should be Issue. So create new file under models directory, and place the following line inside:
class Issue extends Eloquent{};
Now you have starting point for your domain logic. Inside Issue model you can create relations and all related rules (fillable,guarded...).
That is about model, I dont know have migrate anything to DB.
Related
I know this is a really bad idea, but I ran out of ideas, so I'd like to hear some opinions about it.
I'm working on a PHP project which allows the development of addons so that other developers can add their own functions, not so different from thousands of other open-source projects:
root
└── addons
└── addon-a
└── addon-b
└── addon-c
I have to implement some changes to an already made addon (let's say Addon A) without changing the original files or using Composer PSR-4. Unfortunately, the original author did not follow good practices that would help to accomplish this task. Here's how the Addon A is:
Structure
addon-a
└── main.php
└── classes
└── Foo.php
└── Bar.php
addon-a/main.php
include_once __DIR__ . '/classes/Foo.php';
function run() {
echo AddonA\Classes\Foo::returnTwo();
}
addon-a/classes/Foo.php
namespace AddonA\Classes
use AddonA\OtherFolder\FooBar
abstract class Foo {
public static function returnTwo() {
return 2;
}
public static function returnFooBar() {
return new FooBar();
}
}
I need to override the returnTwo function and return another value. Without changing other files, the only way I was able to do it was through the class_alias method, copying the whole Foo class:
First, I created another addon and made it ran before the Addon A "run" function:
Structure
new-addon-a
└── main.php
└── classes
└── Foo.php
new-addon-a/main.php
function init() {
include_once __DIR__ . '/classes/Foo.php';
class_alias( 'NewAddonA\Classes\Foo', 'AddonA\Classes\Foo', true );
}
init();
new-addon-a/classes/Foo.php
namespace NewAddonA\Classes
use AddonA\OtherFolder\FooBar // No changes to the previous "uses."
abstract class Foo {
public static function returnTwo() {
return 3;
}
public static function returnBar() {
return new AddonA\Classes\Bar(); // Here I had to change because the namespace has changed
}
}
Now, when Addon A executes the function run, the AddonA\Classes\Foo::returnTwo will be my own class.
Well, as I said before, this is pretty ugly, and I would lose any update to the Foo code since I'm overriding the entire class. However, this was the only way I thought. Do you think it would be possible to use another method?
Thanks!
My composer project consists of the src and tests folders.
The code inside src is auto-loaded using composers psr4 autoloader like so
"autoload": {
"psr-4": {
"christmas\\":"src/"
}
}
The class to be tested looks like so
namespace christmas;
class Hello
{ //constructor & dependency injection included in real class
public function sayHello()
{
return "HELLO";
}
}
And Finally my test class looks like so
<?php
use PHPUnit\Framework\TestCase;
require_once('../vendor/autoload.php');
use christmas\Hello;
class TestHello extends TestCase
{
public function testSayHelloMethod()
{
$hello = $this->getMockBuilder('Hello')
->getMock();
$hello->expects($this->once())
->method('sayHello')
->will($this->returnValue('HELLO'));
$this->assertEquals(
"HELLO",
$hello->sayHello()
);
}
}
And here is how my i run phpunit
phpunit tests/TestHello
phpunit echoes back the following output
Time: 45 ms, Memory: 4.00 MB
There was 1 warning:
1) tests\Hello::testSayHelloMethod()
Trying to configure method "sayHello" which cannot be configured because it does not exist, has not been specified, is final, or is static
/usr/share/php/PHPUnit/TextUI/TestRunner.php:641
/usr/share/php/PHPUnit/TextUI/Command.php:206
/usr/share/php/PHPUnit/TextUI/Command.php:162
WARNINGS!
Tests: 1, Assertions: 0, Warnings: 1.
Below is a view of how my code is organized,
├── composer.json
├── src
│ └── Hello.php
├── tests
│ └── TestHello.php
└── vendor
What am i missing ? I need to have a passing test with no single warning.
You need to pass the full, namespaced name of the class to the mock builder, i.e. 'christmas\Hello'. Since you need to pass it as a string, just using "Hello" isn't enough to properly identify the class.
Since you're already have a use for the class, you can use ::class to get the full name. For example:
$hello = $this->getMockBuilder(Hello::class)
->getMock();
I'm trying to reproduce isolated test suites with Behat like I have with Cucumber. I've been trying to tweak the behat.yml file using suites and paths as well as moving files and folders around. Behat results make so little sense that I am starting to conclude that it was not designed with this in mind at all. And the documentation does not describe expected files & folder layouts
For each test suite I want:
a specific xxx.feature file
a specific xxxContext.php file, defining a its own xxxContext class
I don't want a monolithic FeatureContext.php file. My project is so huge that it would become a nightmare to maintain, not to mention the potential collateral damage between steps. It's OK to have a single behat.yml file if that helps.
I also read this post but I still haven't solved the problem. How I can create isolated test suites with Behat?
Note: I am using behat 3.0-dev
Edit: Here is the "best" I could achieve. I have two suites, one called "OAuth2" and another called "Achievements".
beyat.yml:
default:
suites:
OAuth2:
contexts:
- OAuth2Context
Achievements:
contexts:
- AchievementsContext
File hierarchy:
├── behat.yml
└── features
├── Achievements.feature
├── OAuth2.feature
└── bootstrap
└── OAuth2Context.php <-- Contains the OAuth2Context and AchievementsContext classes. Also, classes are found only if I name this file this way, I don't know why.
My main problem is that when I run behat, it complains that there are missing steps. Basically, the OAuth2Context class is missing steps that were defined in Achievements and the AchievementsContext is missing steps that were defined in OAuth2. This output completely baffles me:
--- OAuth2Context has missing steps. Define them with these snippets:
/**
* #When I say yes
*/
public function iSayYes()
{
throw new PendingException();
}
/**
* #Then you say no
*/
public function youSayNo()
{
throw new PendingException();
}
--- AchievementsContext has missing steps. Define them with these snippets:
/**
* #When I ask for an Access Token with my credentials
*/
public function iAskForAnAccessTokenWithMyCredentials2()
{
throw new PendingException();
}
/**
* #Then I receive an Access Token
*/
public function iReceiveAnAccessToken2()
{
throw new PendingException();
}
/**
* #When I ask for an Access Token with a Partner User grant type where:
*/
public function iAskForAnAccessTokenWithAPartnerUserGrantTypeWhere(TableNode $table)
{
throw new PendingException();
}
I believe that this is because I defined two separate classes in the php file. But I really don't want to make a single monolitic class. In fact, I'd even like two separate PHP files. The Classes are defined this way:
class OAuth2Context implements Context, SnippetAcceptingContext
{
...
/**
* #When I ask for an Access Token with my credentials
*/
public function iAskForAnAccessTokenWithMyCredentials()
{
...
}
...
}
class AchievementsContext implements Context, SnippetAcceptingContext
{
...
/**
* #When I say yes
*/
public function iSayYes()
{
...
}
...
}
Other (smaller) issue is that I have to name my php file OAuth2Context.php instead of FeatureContext.php, otherwise the classes are not found at all. I don't understand this, as it was my understanding that FeatureContext.php was behat's default name.
Edit: SOLUTION, thanks to Jakub Zalas :
Here is the file layout that works:
├── behat.yml
└── features
├── achievements
│ └── Achievements.feature
├── bootstrap
│ ├── AchievementsContext.php <-- AchievementsContext class
│ └── OAuth2Context.php <-- OAuth2Context class
└── oauth
└── OAuth2.feature
With the following beyat.yml file:
default:
suites:
OAuth2:
contexts:
- OAuth2Context
paths: [ %paths.base%/features/oauth ]
Achievements:
contexts:
- AchievementsContext
paths: [ %paths.base%/features/achievements ]
This way I have steps in separated PHP files, which was my main requirement, and separated suites with separated feature files. I am a happy camper!
With the way you currently organised your suites each feature file will be run for each suite.
Use filters or paths to define which feature files should match a given suite:
# behat.yml
default:
suites:
OAuth2:
contexts:
- OAuth2Context
paths: [ %paths.base%/features/oauth ]
Achievements:
contexts:
- AchievementsContext
paths: [ %paths.base%/features/achievements ]
Having a single feature file per suite is probably not worth it, as you'll end up with lots of suites without a real benefit. Better group features in slightly bigger suites, and register multiple contexts per suite.
How to call function from another controller in Phalcon PHP framework. Here is example for CakePHP http://sherwinrobles.blogspot.com/2013/02/cakephp-calling-function-from-other.html
Based on the link you provided, to my knowledge there is no direct way to call a function in another controller using the request object. However instantiating the controller and calling the function will work just it does in CakePHP
$newController = new \MyNS\Controllers\NewController();
$newController->myFunc();
If you want you can use a static function inside the controller and call it
\MyNS\Controllers\NewController::myFunc();
This is already Tested
For the people that aren't using CakePHP
another way to do this would be to do a helper Folder and write actions, in this case methods.
public/index.php
Add the path helper
$loader->registerDirs(
[
APP_PATH . '/helper/'
]
);
Add a helper Order in apps
└── apps
├── controllers
└─ exampleController.php
├── models
└── helpers
└─ myHelper.php
...
In myHelper.php
<?php
use Phalcon\Di\Injectable;
class myHelper extends Injectable
{
function myNameFunction() {
// here you should write your function
}
}
In exampleController where you want to call the other action, in this case function
<?php
use Phalcon\Mvc\Controller;
class exampleController extends Controller {
public function yourAction() {
//your code
//there are 2 ways of calling the function. either use this one
$myHelper = new myHelper();
$myHelper->myNameFunction();
//or this one
(new UnmatchHelper())->myNameFunction();
}
}
I am trying to structure a large laravel application.
Trying to figure out:
1) Where does business rules / domain rules objects go?
2) How does business rules objects looks like?
By business rules I mean rules like. Given an invoice has possible states [new, approved, completed]. Only invoices in the "approved" state can be emailed to a customer. This is a simplified example.
Since there are a lot of these rules and these rules change often (once or twice a year), I would like to have them in dedicated classes.
This blog post https://stitcher.io/blog/laravel-beyond-crud-01-domain-oriented-laravel provides an application structure similar to my desired application structure. See below.
app/Domain/Invoices/
├── Actions
├── QueryBuilders
├── Collections
├── DataTransferObjects
├── Events
├── Exceptions
├── Listeners
├── Models
├── Rules
└── States
I'm somewhat familiar with domain driven design "theory". I am looking for code examples, preferably in Laravel or PHP (other languages are also ok). If anyone can point me to a github/gitlab project with code examples that would be great as well.
First I'll provide an example, which will be explained below.
<?php
namespace App;
use RuntimeException;
class Invoice
{
private const STATUS_NEW = 'new';
private const STATUS_APPROVED = 'approved';
private const STATUS_COMPLETED = 'completed';
/** #var string */
private $status;
private function __construct(string $status)
{
$this->status = $status;
}
public function create(): self
{
return new self(self::STATUS_NEW);
}
public function approve(): void
{
if ($this->status !== self::STATUS_NEW) {
throw new RuntimeException('Unable to approve invoice, as it is not new');
}
$this->status = self::STATUS_APPROVED;
}
public function complete(): void
{
if ($this->status !== self::STATUS_APPROVED) {
throw new RuntimeException('Unable to complete invoice, as it is not new');
}
$this->status = self::STATUS_COMPLETED;
}
public function canBeSentToCustomer(): bool
{
return $this->status === self::STATUS_COMPLETED;
}
}
So there's a few ideas behind the code above:
The class and method names reflect the domain language. Example: somebody in sales will talk about approving an invoice, which translates to $invoice->approve()
The class is responsible for maintaining a valid state itself. Example: an invoice can't be completed if it's not approved yet
The code does not care about frameworks, databases, web requests, emails or html. All it should do is implement behavior without worrying about where the input comes from or output goes to.